diff -Naur bash-4.3/builtins/circle.def mpibash-4.3/builtins/circle.def --- bash-4.3/builtins/circle.def 1969-12-31 17:00:00.000000000 -0700 +++ mpibash-4.3/builtins/circle.def 2014-05-13 11:27:37.314100671 -0600 @@ -0,0 +1,620 @@ +This file is circle.def, from which is created circle.c. +It implements all of the "circle_*" builtins in Bash. + +$PRODUCES circle.c + +#include + +#include +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include "../bashintl.h" +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" +#include + +extern int running_trap, trap_saved_exit_value; + +static int circle_rank; /* Rank in the Libcircle job */ +static SHELL_VAR *create_func = NULL; /* User-defined callback function for CIRCLE_cb_create. */ +static SHELL_VAR *process_func = NULL; /* User-defined callback function for CIRCLE_cb_process. */ +static SHELL_VAR *reduce_init_func = NULL; /* User-defined callback function for CIRCLE_cb_reduce_init. */ +static SHELL_VAR *reduce_fini_func = NULL; /* User-defined callback function for CIRCLE_cb_reduce_fini. */ +static SHELL_VAR *reduce_op_func = NULL; /* User-defined callback function for CIRCLE_cb_reduce_op. */ +static CIRCLE_handle *current_handle = NULL; /* Active handle within a callback or NULL if not within a callback */ +static int within_reduction = 0; /* 1=within a reduction callback; 0=not */ + +/* Return with a usage message if no arguments remain. */ +#define YES_ARGS(LIST) \ + if ((LIST) == 0) \ + { \ + builtin_usage (); \ + return (EX_USAGE); \ + } + +/* Perform the same operation as bind_variable, but with VALUE being a + * number, not a string. */ +static SHELL_VAR * +bind_variable_number (name, value, flags) + const char *name; + long value; + int flags; +{ + char numstr[25]; /* String version of VALUE */ + + sprintf (numstr, "%ld", value); + return bind_variable (name, numstr, flags); +} + +/* Invoke the user-defined creation-callback function (create_func). */ +static void +internal_create_func (handle) + CIRCLE_handle *handle; +{ + WORD_LIST *funcargs; + + if (create_func == NULL) + return; + current_handle = handle; + funcargs = make_word_list (make_word ("cb_create"), NULL); + execute_shell_function (create_func, funcargs); + dispose_words (funcargs); + current_handle = NULL; +} + +/* Invoke the user-defined process-callback function (process_func). */ +static void +internal_process_func (handle) + CIRCLE_handle *handle; +{ + WORD_LIST *funcargs; + + if (process_func == NULL) + return; + current_handle = handle; + funcargs = make_word_list (make_word ("cb_process"), NULL); + execute_shell_function (process_func, funcargs); + dispose_words (funcargs); + current_handle = NULL; +} + +/* Invoke the user-defined reduction-initiation callback function + * (reduce_init_func). */ +static void +internal_reduce_init_func (void) +{ + WORD_LIST *funcargs; + + if (reduce_init_func == NULL) + return; + within_reduction = 1; + funcargs = make_word_list (make_word ("cb_reduce_init"), NULL); + execute_shell_function (reduce_init_func, funcargs); + dispose_words (funcargs); + within_reduction = 0; +} + +/* Invoke the user-defined reduction callback function + * (reduce_op_func). */ +static void +internal_reduce_op_func (buf1, size1, buf2, size2) + const void* buf1; + size_t size1; + const void* buf2; + size_t size2; +{ + WORD_LIST *funcargs; + + if (reduce_op_func == NULL) + return; + within_reduction = 1; + funcargs = make_word_list (make_word (buf2), NULL); + funcargs = make_word_list (make_word (buf1), funcargs); + funcargs = make_word_list (make_word ("cb_reduce_op"), funcargs); + execute_shell_function (reduce_op_func, funcargs); + dispose_words (funcargs); + within_reduction = 0; +} + +/* Invoke the user-defined reduction-finalization callback function + * (reduce_fini_func). */ +static void +internal_reduce_fini_func (buf, size) + const void* buf; + size_t size; +{ + WORD_LIST *funcargs; + + if (reduce_fini_func == NULL) + return; + funcargs = make_word_list (make_word (buf), NULL); + funcargs = make_word_list (make_word ("cb_reduce_fini"), funcargs); + execute_shell_function (reduce_fini_func, funcargs); + dispose_words (funcargs); +} + +/* Look up a user-provided callback function. */ +static int +find_callback_function (list, user_func) + WORD_LIST *list; + SHELL_VAR **user_func; +{ + char *funcname; /* Name of the user-defined function. */ + + /* If no argument was provided, nullify the callback function. */ + if (list == NULL) + { + *user_func = NULL; + return EXECUTION_SUCCESS; + } + + /* Get the callback function. */ + funcname = list->word->word; + list = list->next; + no_args (list); + *user_func = find_function (funcname); + if (*user_func == NULL) + { + builtin_error (_("function %s not found"), funcname); + return EXECUTION_FAILURE; + } + return EXECUTION_SUCCESS; +} + +/* Initialize Libcircle. */ +void +initialize_libcircle (argc, argv) + int argc; + char **argv; +{ + circle_rank = CIRCLE_init (argc, argv, CIRCLE_DEFAULT_FLAGS); + bind_variable_number ("circle_rank", circle_rank, 0); + CIRCLE_enable_logging (CIRCLE_LOG_WARN); + CIRCLE_cb_create (internal_create_func); + CIRCLE_cb_process (internal_process_func); + CIRCLE_cb_reduce_init (internal_reduce_init_func); + CIRCLE_cb_reduce_op (internal_reduce_op_func); + CIRCLE_cb_reduce_fini (internal_reduce_fini_func); +} + +/* Finalize Libcircle. */ +void +finalize_libcircle (void) +{ + CIRCLE_finalize (); +} + +/* ---------------------------------------------------------------------- */ + +$BUILTIN circle_set_options +$FUNCTION circle_set_options_builtin +$SHORT_DOC circle_set_options [flag]... +Change Libcircle's run-time behavior. + +Arguments: + FLAG "split_random", "split_equal", or "create_global" + +Multiple flags can be provided. If no flags are provided, Libcircle +reverts to its default options. + +Exit Status: +Returns 0 unless an invalid option is given. +$END +/*'*/ + +/* Here is the circle_set_options builtin. */ +int +circle_set_options_builtin (list) + WORD_LIST *list; +{ + char *word; /* One argument */ + int flags = 0; /* Flags to pass to CIRCLE_set_options */ + + if (list == NULL) + flags = CIRCLE_DEFAULT_FLAGS; + else + while (list != NULL) + { + word = list->word->word; + if (!strcmp (word, "split_random")) + flags |= CIRCLE_SPLIT_RANDOM; + else if (!strcmp (word, "split_equal")) + flags |= CIRCLE_SPLIT_EQUAL; + else if (!strcmp (word, "create_global")) + flags |= CIRCLE_CREATE_GLOBAL; + else + { + builtin_error (_("invalid flag \"%s\""), word); + return (EXECUTION_FAILURE); + } + list = list->next; + } + CIRCLE_set_options (flags); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_cb_create +$FUNCTION circle_cb_create_builtin +$SHORT_DOC circle_cb_create [func] +Register a function that will create work when asked. + +Arguments: + FUNC User-defined callback function that will invoke + circle_enqueue when called + +If FUNC is omitted, no function will be associated with work creation. +This can be used to nullify a previous circle_cb_create invocation. + +Exit Status: +Returns 0 unless an invalid function is given or an error occurs. +$END + +/* Here is the circle_cb_create builtin. */ +int +circle_cb_create_builtin (list) + WORD_LIST *list; +{ + return find_callback_function (list, &create_func); +} + +$BUILTIN circle_cb_process +$FUNCTION circle_cb_process_builtin +$SHORT_DOC circle_cb_process [func] +Register a function that will process work when asked. + +Arguments: + FUNC User-defined callback function that will invoke + circle_enqueue when called + +If FUNC is omitted, no function will be associated with work processing. +This can be used to nullify a previous circle_cb_process invocation. + +Exit Status: +Returns 0 unless an invalid function is given or an error occurs. +$END + +/* Here is the circle_cb_process builtin. */ +int +circle_cb_process_builtin (list) + WORD_LIST *list; +{ + return find_callback_function (list, &process_func); +} + +$BUILTIN circle_begin +$FUNCTION circle_begin_builtin +$SHORT_DOC circle_begin +Begin creation and processing of the distributed work queue. + +Exit Status: +Returns 0 unless an error occurs. +$END + +/* Here is the circle_begin builtin. */ +int +circle_begin_builtin (list) + WORD_LIST *list; +{ + no_args (list); + CIRCLE_begin (); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_enqueue +$FUNCTION circle_enqueue_builtin +$SHORT_DOC circle_enqueue work +Enqueue work onto the distributed queue. + +Arguments: + WORK "Work" as represented by an arbitrary string of limited + size (generally around 4KB) + +Exit Status: +Returns 0 unless an error occurs. +$END + +/* Here is the circle_enqueue builtin. */ +int +circle_enqueue_builtin (list) + WORD_LIST *list; +{ + char *work; /* Work to perform */ + + /* Extract the work argument. */ + YES_ARGS (list); + work = list->word->word; + list = list->next; + no_args (list); + + /* Complain if we're not within a proper callback function. */ + if (current_handle == NULL) + { + builtin_error (_("not within a Libcircle \"create\" or \"process\" callback function")); + return EXECUTION_FAILURE; + } + + /* Enqueue the work. */ + if (current_handle->enqueue (work) == -1) + return EXECUTION_FAILURE; + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_dequeue +$FUNCTION circle_dequeue_builtin +$SHORT_DOC circle_dequeue var +Dequeue work from the distributed queue into a variable. + +Arguments: + VAR Variable in which to receive previously enqueued "work" + +Exit Status: +Returns 0 unless an error occurs. +$END + +/* Here is the circle_dequeue builtin. */ +int +circle_dequeue_builtin (list) + WORD_LIST *list; +{ + char *varname; /* Variable in which to store the work string */ + char work[CIRCLE_MAX_STRING_LEN+1]; /* Work to perform */ + + /* Extract the variable-name argument. */ + YES_ARGS (list); + varname = list->word->word; + list = list->next; + no_args (list); + + /* Complain if we're not within a callback function. */ + if (current_handle == NULL) + { + builtin_error (_("not within a Libcircle callback function")); + return EXECUTION_FAILURE; + } + + /* Dequeue the work and bind it to the given variable. */ + if (current_handle->dequeue (work) == -1) + return EXECUTION_FAILURE; + bind_variable (varname, work, 0); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_enable_logging +$FUNCTION circle_enable_logging_builtin +$SHORT_DOC circle_enable_logging log_level +Change Libcircle's logging verbosity + +Arguments: + LOG_LEVEL "fatal", "error", "warning", "info", or "debug" + +Exit Status: +Returns 0 unless an invalid option is given. +$END +/*'*/ + +/* Here is the circle_enable_logging builtin. */ +int +circle_enable_logging_builtin (list) + WORD_LIST *list; +{ + char *word; /* One argument */ + CIRCLE_loglevel loglevel; /* Level to set */ + + /* Parse the log level. */ + YES_ARGS (list); + word = list->word->word; + if (!strcmp (word, "fatal")) + loglevel = CIRCLE_LOG_FATAL; + else if (!strcmp (word, "error")) + loglevel = CIRCLE_LOG_ERR; + else if (!strcmp (word, "warning")) + loglevel = CIRCLE_LOG_WARN; + else if (!strcmp (word, "info")) + loglevel = CIRCLE_LOG_INFO; + else if (!strcmp (word, "debug")) + loglevel = CIRCLE_LOG_DBG; + else + { + builtin_error (_("invalid log level \"%s\""), word); + return (EXECUTION_FAILURE); + } + + /* Set the log level. */ + CIRCLE_enable_logging (loglevel); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_abort +$FUNCTION circle_abort_builtin +$SHORT_DOC circle_abort +Terminate queue processing. + +Exit Status: +Returns 0 unless an error occurs. +$END + +/* Here is the circle_abort builtin. */ +int +circle_abort_builtin (list) + WORD_LIST *list; +{ + no_args (list); + CIRCLE_abort (); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_checkpoint +$FUNCTION circle_checkpoint_builtin +$SHORT_DOC circle_checkpoint +Checkpoint a work queue to disk. + +Write a file called circle${circle_rank}.txt containing the current +queue state of rank ${circle_rank}. On a later run, a worker can +invoke circle_read_restarts to repopulate its queue from such a +checkpoint file. + +Exit Status: +Returns 0 unless an error occurs. +$END +/*'*/ + +/* Here is the circle_checkpoint builtin. */ +int +circle_checkpoint_builtin (list) + WORD_LIST *list; +{ + no_args (list); + CIRCLE_checkpoint (); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_read_restarts +$FUNCTION circle_read_restarts_builtin +$SHORT_DOC circle_read_restarts +Repopulate a work queue from a disk checkpoint. + +Read queue contents from a file called circle${circle_rank}.txt, which +was previously produced by circle_checkpoint. + +Exit Status: +Returns 0 unless an error occurs. +$END +/*'*/ + +/* Here is the circle_read_restarts builtin. */ +int +circle_read_restarts_builtin (list) + WORD_LIST *list; +{ + no_args (list); + CIRCLE_read_restarts (); + return EXECUTION_SUCCESS; +} + +$BUILTIN circle_cb_reduce_init +$FUNCTION circle_cb_reduce_init_builtin +$SHORT_DOC circle_cb_reduce_init [func] +Register a function that will initiate a reduction operation. + +Arguments: + FUNC User-defined callback function that will invoke + circle_reduce when called + +FUNC will be invoked on all ranks. + +If FUNC is omitted, no function will be associated with reduction +initialization. This can be used to nullify a previous +circle_cb_reduce_init invocation. + +Exit Status: +Returns 0 unless an invalid function is given or an error occurs. +$END + +/* Here is the circle_cb_reduce_init builtin. */ +int +circle_cb_reduce_init_builtin (list) + WORD_LIST *list; +{ + return find_callback_function (list, &reduce_init_func); +} + +$BUILTIN circle_cb_reduce_op +$FUNCTION circle_cb_reduce_op_builtin +$SHORT_DOC circle_cb_reduce_op [func] +Register a function that will complete a reduction operation. + +Arguments: + FUNC User-defined callback function that will receive + two items to reduce and invoke circle_reduce on + the reduced value + +If FUNC is omitted, no function will be associated with reduction +execution. This can be used to nullify a previous circle_cb_reduce_op +invocation. + +Exit Status: +Returns 0 unless an invalid function is given or an error occurs. +$END + +/* Here is the circle_cb_reduce_op builtin. */ +int +circle_cb_reduce_op_builtin (list) + WORD_LIST *list; +{ + return find_callback_function (list, &reduce_op_func); +} + +$BUILTIN circle_cb_reduce_fini +$FUNCTION circle_cb_reduce_fini_builtin +$SHORT_DOC circle_cb_reduce_fini [func] +Register a function that will complete a reduction operation. + +Arguments: + FUNC User-defined callback function that will receive + the final reduced data + +If FUNC is omitted, no function will be associated with reduction +completion. This can be used to nullify a previous +circle_cb_reduce_fini invocation. + +Libcircle guarantees that FUNC will be invoked only on rank 0. + +Exit Status: +Returns 0 unless an invalid function is given or an error occurs. +$END + +/* Here is the circle_cb_reduce_fini builtin. */ +int +circle_cb_reduce_fini_builtin (list) + WORD_LIST *list; +{ + return find_callback_function (list, &reduce_fini_func); +} + +$BUILTIN circle_reduce +$FUNCTION circle_reduce_builtin +$SHORT_DOC circle_reduce work +Seed the next phase of a reduction operation + +Arguments: + WORK "Work" as represented by an arbitrary string of limited + size (generally around 4KB) + +This function should be called both by the callback function +registered with circle_reduce_init and the callback function +registered with circle_reduce_op. + +Exit Status: +Returns 0 unless an error occurs. +$END + +/* Here is the circle_reduce builtin. */ +int +circle_reduce_builtin (list) + WORD_LIST *list; +{ + char *work; /* Work to perform */ + + /* Extract the work argument. */ + YES_ARGS (list); + work = list->word->word; + list = list->next; + no_args (list); + + /* Complain if we're not within a proper callback function. */ + if (!within_reduction) + { + builtin_error (_("not within a Libcircle \"reduce_init\" or \"reduce_op\" callback function")); + return EXECUTION_FAILURE; + } + + /* Reduce the work. */ + CIRCLE_reduce (work, strlen (work)); + return EXECUTION_SUCCESS; +} diff -Naur bash-4.3/builtins/Makefile.in mpibash-4.3/builtins/Makefile.in --- bash-4.3/builtins/Makefile.in 2012-05-25 07:29:19.000000000 -0600 +++ mpibash-4.3/builtins/Makefile.in 2014-05-13 11:27:37.314100671 -0600 @@ -141,7 +141,9 @@ $(srcdir)/times.def $(srcdir)/trap.def $(srcdir)/type.def \ $(srcdir)/ulimit.def $(srcdir)/umask.def $(srcdir)/wait.def \ $(srcdir)/reserved.def $(srcdir)/pushd.def $(srcdir)/shopt.def \ - $(srcdir)/printf.def $(srcdir)/complete.def $(srcdir)/mapfile.def + $(srcdir)/printf.def $(srcdir)/complete.def $(srcdir)/mapfile.def \ + $(srcdir)/mpi.def \ +@CIRCLE@ $(srcdir)/circle.def STATIC_SOURCE = common.c evalstring.c evalfile.c getopt.c bashgetopt.c \ getopt.h @@ -153,7 +155,9 @@ jobs.o kill.o let.o mapfile.o \ pushd.o read.o return.o set.o setattr.o shift.o source.o \ suspend.o test.o times.o trap.o type.o ulimit.o umask.o \ - wait.o getopts.o shopt.o printf.o getopt.o bashgetopt.o complete.o + wait.o getopts.o shopt.o printf.o getopt.o bashgetopt.o complete.o \ + mpi.o \ +@CIRCLE@ circle.o CREATED_FILES = builtext.h builtins.c psize.aux pipesize.h tmpbuiltins.c \ tmpbuiltins.h @@ -317,6 +321,8 @@ getopts.o: getopts.def reserved.o: reserved.def complete.o: complete.def +@CIRCLE@ circle.o: circle.def +mpi.o: mpi.def # C files bashgetopt.o: ../config.h $(topdir)/bashansi.h $(BASHINCDIR)/ansi_stdlib.h @@ -644,6 +650,19 @@ mapfile.o: $(topdir)/subst.h $(topdir)/externs.h $(BASHINCDIR)/maxpath.h mapfile.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/variables.h $(topdir)/conftypes.h mapfile.o: $(topdir)/arrayfunc.h ../pathnames.h +@CIRCLE@ circle.o: $(topdir)/command.h ../config.h $(BASHINCDIR)/memalloc.h $(topdir)/error.h +@CIRCLE@ circle.o: $(topdir)/general.h $(topdir)/xmalloc.h $(topdir)/subst.h $(topdir)/externs.h +@CIRCLE@ circle.o: $(topdir)/quit.h $(topdir)/dispose_cmd.h $(topdir)/make_cmd.h +@CIRCLE@ circle.o: $(topdir)/shell.h $(topdir)/syntax.h $(topdir)/unwind_prot.h $(topdir)/variables.h $(topdir)/conftypes.h +@CIRCLE@ circle.o: $(BASHINCDIR)/maxpath.h ../pathnames.h +mpi.o: ../config.h ../config-top.h ../config-bot.h ../bashintl.h +mpi.o: ../include/gettext.h ../shell.h ../config.h ../bashjmp.h +mpi.o: ../include/posixjmp.h ../command.h ../syntax.h ../general.h +mpi.o: ../bashtypes.h ../include/chartypes.h ../xmalloc.h ../bashansi.h +mpi.o: ../error.h ../variables.h ../array.h ../assoc.h ../hashlib.h +mpi.o: ../conftypes.h ../arrayfunc.h ../quit.h ../sig.h ../include/maxpath.h +mpi.o: ../unwind_prot.h ../dispose_cmd.h ../make_cmd.h ../include/ocache.h +mpi.o: ../subst.h ../pathnames.h ../externs.h common.h bashgetopt.h #bind.o: $(RL_LIBSRC)chardefs.h $(RL_LIBSRC)readline.h $(RL_LIBSRC)keymaps.h diff -Naur bash-4.3/builtins/mpi.def mpibash-4.3/builtins/mpi.def --- bash-4.3/builtins/mpi.def 1969-12-31 17:00:00.000000000 -0700 +++ mpibash-4.3/builtins/mpi.def 2014-05-13 11:27:37.314100671 -0600 @@ -0,0 +1,744 @@ +This file is mpi.def, from which is created mpi.c. +It implements all of the "mpi_*" builtins in Bash. + +$PRODUCES mpi.c + +#include + +#include +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif +# include +#endif + +#include "../bashintl.h" +#include "../shell.h" +#include "common.h" +#include "bashgetopt.h" +#include + +extern int running_trap, trap_saved_exit_value; + +/* Keep track of who we are within MPI_COMM_WORLD. */ +static int mpi_rank; +static int mpi_num_ranks; + +/* Try an MPI operation. Return with an error message on failure. */ +#define MPI_TRY(STMT) \ + do \ + { \ + int mpierr; \ + mpierr = STMT; \ + if (mpierr != MPI_SUCCESS) \ + return report_mpi_error (mpierr); \ + } \ + while (0) + +/* Return with a usage message if no arguments remain. */ +#define YES_ARGS(LIST) \ + if ((LIST) == 0) \ + { \ + builtin_usage (); \ + return (EX_USAGE); \ + } + +/* Return with an error message if a given variable is read-only or if + * we can't write to it for any other reason (e.g., it's defined as a + * function). */ +#define REQUIRE_WRITABLE(NAME) \ + do \ + { \ + SHELL_VAR *bindvar = find_shell_variable (NAME); \ + if (bindvar) \ + { \ + if (readonly_p (bindvar)) \ + { \ + err_readonly (NAME); \ + return (EXECUTION_FAILURE); \ + } \ + if (unbind_variable (NAME) == -1) \ + { \ + builtin_error ("Failed to write to variable %s", NAME); \ + return (EXECUTION_FAILURE); \ + } \ + } \ + } \ + while (0) + +/* Initialize MPI. */ +void +initialize_mpi (argc, argv) + int argc; + char **argv; +{ + int init_done; + + MPI_Initialized (&init_done); + if (!init_done) + MPI_Init (&argc, &argv); + MPI_Errhandler_set (MPI_COMM_WORLD, MPI_ERRORS_RETURN); + MPI_Comm_rank (MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size (MPI_COMM_WORLD, &mpi_num_ranks); +} + +/* Finalize MPI. */ +void +finalize_mpi () +{ + MPI_Finalize (); +} + +/* Parse an operation name into an MPI_Op. Return 1 on success, 0 on + * failure. */ +static int +parse_operation (char *name, MPI_Op *op) +{ + /* Define a mapping from operator names to MPI_Op values. */ + typedef struct { + char *name; /* Operation name (e.g., "sum") */ + MPI_Op value; /* Operation value (e.g., MPI_SUM) */ + } opname2value_t; + static opname2value_t oplist[] = { + {"max", MPI_MAX}, + {"min", MPI_MIN}, + {"sum", MPI_SUM}, + {"prod", MPI_PROD}, + {"land", MPI_LAND}, + {"band", MPI_BAND}, + {"lor", MPI_LOR}, + {"bor", MPI_BOR}, + {"lxor", MPI_LXOR}, + {"bxor", MPI_BXOR}, + {"maxloc", MPI_MAXLOC}, + {"minloc", MPI_MINLOC} + }; + size_t i; + + for (i = 0; i < sizeof(oplist)/sizeof(opname2value_t); i++) + if (!strcmp(name, oplist[i].name)) + { + *op = oplist[i].value; + if (i > 0) + { + /* As a performance optimization, bubble up the value we + * just found. */ + opname2value_t prev = oplist[i - 1]; + oplist[i - 1] = oplist[i]; + oplist[i] = prev; + } + return 1; + } + return 0; +} + +/* Report an error to the user and return EXECUTION_FAILURE. */ +static int +report_mpi_error (mpierr) + int mpierr; +{ + char errstr[MPI_MAX_ERROR_STRING]; + int errstrlen; + + MPI_Error_string (mpierr, errstr, &errstrlen); + builtin_error ("%s", errstr); + return EXECUTION_FAILURE; +} + +/* Perform the same operation as bind_variable, but with VALUE being a + * number, not a string. */ +static SHELL_VAR * +bind_variable_number (name, value, flags) + const char *name; + long value; + int flags; +{ + char numstr[25]; /* String version of VALUE */ + + sprintf (numstr, "%ld", value); + return bind_variable (name, numstr, flags); +} + +/* Perform the same operation as bind_array_variable, but with VALUE + * being a number, not a string. */ +static SHELL_VAR * +bind_array_variable_number (name, ind, value, flags) + char *name; + arrayind_t ind; + long value; + int flags; +{ + char numstr[25]; /* String version of VALUE */ + + sprintf (numstr, "%ld", value); + return bind_array_variable (name, ind, numstr, flags); +} + +/* Define a reduction-type function (allreduce, scan, exscan, etc.). */ +typedef int (*reduction_func_t)(void *, void *, int, MPI_Datatype, MPI_Op, MPI_Comm); + +/* Perform any reduction-type operation (allreduce, scan, exscan, etc.). */ +static int +reduction_like (list, funcname, func) + WORD_LIST *list; + char *funcname; + reduction_func_t func; +{ + char *word; /* One argument */ + struct { + long int value; /* Reduced value */ + int rank; /* Rank associated with the above */ + } number, result; + MPI_Op operation = MPI_SUM; /* Operation to perform */ + char *varname; /* Name of the variable to bind the results to */ + intmax_t n; + int i; + + /* Parse "-O OPERATION" (optional), where OPERATION is a reduction + * operation. */ + YES_ARGS (list); + word = list->word->word; + if (ISOPTION (word, 'O')) + { + list = list->next; + if (list == 0) + { + sh_needarg (funcname); + return (EX_USAGE); + } + word = list->word->word; + if (!parse_operation (word, &operation)) + { + sh_invalidopt ("-O"); + return (EX_USAGE); + } + list = list->next; + } + + /* Parse the argument, which must be a number. */ + YES_ARGS (list); + word = list->word->word; + if (!legal_number (word, &n)) + { + sh_neednumarg (funcname); + return (EX_USAGE); + } + number.value = (long int) n; + number.rank = mpi_rank; + list = list->next; + + /* Parse the target variable, which must not be read-only. */ + YES_ARGS (list); + varname = list->word->word; + if (mpi_rank != 0 || func != MPI_Exscan) + REQUIRE_WRITABLE (varname); + list = list->next; + no_args (list); + + /* Perform the reduction operation. Bind the given array variable + * to the result and, for minloc/maxloc, the associated rank. */ + if (mpi_rank != 0 || func != MPI_Exscan) { + bind_array_variable (varname, 0, "", 0); + bind_array_variable (varname, 1, "", 0); + } + if (operation == MPI_MINLOC || operation == MPI_MAXLOC) + { + MPI_TRY (func (&number, &result, 1, MPI_LONG_INT, operation, MPI_COMM_WORLD)); + if (mpi_rank != 0 || func != MPI_Exscan) + bind_array_variable_number (varname, 1, result.rank, 0); + } + else + MPI_TRY (func (&number.value, &result.value, 1, MPI_LONG, operation, MPI_COMM_WORLD)); + if (mpi_rank != 0 || func != MPI_Exscan) + bind_array_variable_number (varname, 0, result.value, 0); + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_comm_rank +$FUNCTION mpi_comm_rank_builtin +$SHORT_DOC mpi_comm_rank name +Return the process's rank in the MPI job. + +Arguments: + NAME Scalar variable in which to receive the rank + +Exit Status: +Returns 0 unless an invalid option is given. +$END +/*'*/ + +/* Here is the mpi_comm_rank builtin. */ +int +mpi_comm_rank_builtin (list) + WORD_LIST *list; +{ + char *varname; /* Name of the variable to bind the results to */ + + YES_ARGS (list); + varname = list->word->word; + REQUIRE_WRITABLE (varname); + list = list->next; + no_args (list); + bind_variable_number (varname, mpi_rank, 0); + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_comm_size +$FUNCTION mpi_comm_size_builtin +$SHORT_DOC mpi_comm_size name +Return the total number of ranks in the MPI job. + +Arguments: + NAME Scalar variable in which to receive the number of ranks + +Exit Status: +Returns 0 unless an invalid option is given. +$END + +/* Here is the mpi_comm_size builtin. */ +int +mpi_comm_size_builtin (list) + WORD_LIST *list; +{ + char *varname; /* Name of the variable to bind the results to */ + + YES_ARGS (list); + varname = list->word->word; + REQUIRE_WRITABLE (varname); + list = list->next; + no_args (list); + bind_variable_number (varname, mpi_num_ranks, 0); + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_abort +$FUNCTION mpi_abort_builtin +$SHORT_DOC mpi_abort [n] +Abort all processes in the MPI job and exit the shell. + +Exits not only the caller's shell (with a status of N) but also all +remote shells that are part of the same MPI job. If N is omitted, the +exit status is that of the last command executed. + +This command should be used only in extreme circumstances. It is +better for each process to exit normally on its own. +$END +/*'*/ + +/* Here is the mpi_abort builtin. */ +int +mpi_abort_builtin (list) + WORD_LIST *list; +{ + int exit_value; + + exit_value = (running_trap == 1 && list == 0) ? trap_saved_exit_value : get_exitstat (list); /* Copied from exit.def */ + MPI_TRY (MPI_Abort (MPI_COMM_WORLD, exit_value)); + return EXECUTION_FAILURE; +} + +$BUILTIN mpi_send +$FUNCTION mpi_send_builtin +$SHORT_DOC mpi_send [-t tag] rank message +Send a message to a remote process in the same MPI job. + +Options: + -t TAG Send the message using tag TAG (default: 0). TAG must + be a nonnegative integer. + +Arguments: + RANK Whom to send the message to. RANK must be an integer in + the range [0, $(mpi_comm_size)-1]. + + MESSAGE String to send to rank RANK. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_send builtin. */ +int +mpi_send_builtin (list) + WORD_LIST *list; +{ + char *word; /* One argument */ + intmax_t target_rank; /* MPI target rank */ + char *message; /* Message to send to rank target_rank */ + intmax_t tag = 0; /* Message tag to use */ + + /* Parse "-t TAG" (optional), where TAG is a number or "any". */ + YES_ARGS (list); + word = list->word->word; + if (ISOPTION (word, 't')) + { + list = list->next; + if (list == 0) + { + sh_needarg ("mpi_recv"); + return (EX_USAGE); + } + word = list->word->word; + if (!legal_number (word, &tag)) + { + sh_neednumarg ("-t"); + return (EX_USAGE); + } + list = list->next; + } + else if (*word == '-') + { + sh_invalidopt (word); + builtin_usage (); + return (EX_USAGE); + } + + /* Parse the target rank, which must be a number. */ + YES_ARGS (list); + word = list->word->word; + if (!legal_number (word, &target_rank)) + { + builtin_error (_("mpi_send: numeric rank required")); + return (EX_USAGE); + } + list = list->next; + + /* Parse the message to send. */ + YES_ARGS (list); + message = list->word->word; + list = list->next; + no_args (list); + + /* Send the message. */ + MPI_TRY (MPI_Send (message, strlen(message)+1, MPI_BYTE, (int)target_rank, (int)tag, MPI_COMM_WORLD)); + return EXECUTION_SUCCESS; +} + + +$BUILTIN mpi_recv +$FUNCTION mpi_recv_builtin +$SHORT_DOC mpi_recv [-t tag] rank name +Receive a message from a remote process in the same MPI job. + +Options: + -t TAG Receive only messages sent using tag TAG (default: 0). + TAG must be either a nonnegative integer or the string + "any" to receive messages sent using any tag. + +Arguments: + RANK Receive only messages sent from sender RANK. RANK + must either be in the range [0, $(mpi_comm_size)-1] or + be the string "any" to receive messages from any sender. + + NAME Array variable in which to receive the message, sender + rank, and tag. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_recv builtin. */ +int +mpi_recv_builtin (list) + WORD_LIST *list; +{ + char *word; /* One argument */ + intmax_t source_rank; /* MPI source rank */ + char *endptr; /* Used for parsing strings into numbers */ + MPI_Status status; /* Status of an MPI operation */ + int count; /* Message length in bytes */ + intmax_t tag = 0; /* Message tag to use */ + char *varname; /* Name of the variable to bind the results to */ + static char *message = NULL; /* Message received from MPI */ + static size_t alloced = 0; /* Number of bytes allocated for the above */ + int opt; /* Parsed option */ + + /* Parse any options provided. */ + reset_internal_getopt (); + while ((opt = internal_getopt (list, "t:")) != -1) + { + switch (opt) + { + case 't': + if (!strcmp (list_optarg, "any")) + tag = MPI_ANY_TAG; + else if (!legal_number (list_optarg, &tag)) + { + builtin_error (_("-t: numeric argument or \"any\" required")); + return (EX_USAGE); + } + break; + + default: + sh_invalidopt (word); + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* Parse the source rank, which must be a number or "any". */ + YES_ARGS (list); + word = list->word->word; + if (!legal_number (word, &source_rank)) + { + if (!strcmp (word, "any")) + source_rank = MPI_ANY_SOURCE; + else + { + builtin_error (_("mpi_recv: numeric rank or \"any\" required")); + return (EX_USAGE); + } + } + list = list->next; + + /* Parse the target variable, which must not be read-only. */ + YES_ARGS (list); + varname = list->word->word; + REQUIRE_WRITABLE (varname); + list = list->next; + no_args (list); + + /* Receive a message. Because we don't know long the message will + * be, we first probe to get the length. */ + MPI_TRY (MPI_Probe ((int)source_rank, (int)tag, MPI_COMM_WORLD, &status)); + MPI_TRY (MPI_Get_count (&status, MPI_BYTE, &count)); + if (alloced < count) + { + message = xrealloc (message, count); + alloced = count; + } + MPI_TRY (MPI_Recv (message, count, MPI_BYTE, status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD, &status)); + bind_array_variable (varname, 0, message, 0); + bind_array_variable_number (varname, 1, status.MPI_SOURCE, 0); + bind_array_variable_number (varname, 2, status.MPI_TAG, 0); + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_barrier +$FUNCTION mpi_barrier_builtin +$SHORT_DOC mpi_barrier +Synchronizes all of the processes in the MPI job. + +No process will return from mpi_barrier until all processes have +called mpi_barrier. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_barrier builtin. */ +int +mpi_barrier_builtin (list) + WORD_LIST *list; +{ + no_args (list); + MPI_TRY (MPI_Barrier (MPI_COMM_WORLD)); + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_bcast +$FUNCTION mpi_bcast_builtin +$SHORT_DOC mpi_bcast [message] name +Broadcast a message to all processes in the same MPI job. + +Arguments: + MESSAGE String to broadcast from one process to all the others. + + NAME Scalar variable in which to receive the broadcast message. + +Exactly one process in the MPI job must specify a message to +broadcast. No process will return from mpi_bcast until all processes +have called mpi_bcast. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_bcast builtin. */ +int +mpi_bcast_builtin (list) + WORD_LIST *list; +{ + char *word; /* One argument */ + int root; /* MPI root rank */ + char *root_message; /* Message to broadcast */ + int msglen; /* Length in bytes of the above (including the NULL byte) */ + char *varname; /* Name of the variable to bind the results to */ + static int *all_lengths = NULL; /* List of every rank's msglen */ + static char *message = NULL; /* Message received from the root */ + static int alloced = 0; /* Bytes allocated for the above */ + int i; + + /* Parse the optional message and target variable, which must not be + * read-only. */ + YES_ARGS (list); + if (list->next == NULL) + { + /* Non-root */ + root_message = NULL; + msglen = -1; + } + else + { + /* Root */ + root_message = list->word->word; + msglen = (int) strlen(root_message) + 1; + list = list->next; + } + varname = list->word->word; + REQUIRE_WRITABLE (varname); + list = list->next; + no_args (list); + + /* Acquire global agreement on the root and the message size. */ + if (all_lengths == NULL) + all_lengths = xmalloc (mpi_num_ranks*sizeof(int)); + MPI_TRY (MPI_Allgather (&msglen, 1, MPI_INT, all_lengths, 1, MPI_INT, MPI_COMM_WORLD)); + root = -1; + for (i = 0; i < mpi_num_ranks; i++) + { + if (all_lengths[i] == -1) + continue; + if (root != -1) + { + builtin_error (_("mpi_bcast: more than one process specified a message")); + return (EXECUTION_FAILURE); + } + root = i; + msglen = all_lengths[i]; + } + if (root == -1) + { + builtin_error (_("mpi_bcast: no process specified a message")); + return (EXECUTION_FAILURE); + } + + /* Broadcast the message. */ + if (mpi_rank == root) + { + MPI_TRY (MPI_Bcast (root_message, msglen, MPI_BYTE, root, MPI_COMM_WORLD)); + bind_variable (varname, root_message, 0); + } + else + { + if (alloced < msglen) + { + message = xrealloc (message, msglen); + alloced = msglen; + } + MPI_TRY (MPI_Bcast (message, msglen, MPI_BYTE, root, MPI_COMM_WORLD)); + bind_variable (varname, message, 0); + } + return EXECUTION_SUCCESS; +} + +$BUILTIN mpi_scan +$FUNCTION mpi_scan_builtin +$SHORT_DOC mpi_scan number name +Perform an inclusive scan across all processes in the same MPI job. + + -O OPERATION Operation to perform. Must be one of "max", "min", + "sum", "prod", "land", "band", "lor", "bor", "lxor", + "bxor", "maxloc", or "minloc" (default: "sum"). + +Arguments: + NUMBER Integer to use in the scan operation. + + NAME Array variable in which to receive the result and, in + the case of maxloc and minloc, the associated rank. + +In an inclusive-scan operation, each process i presents a number, +a[i]. Once all processes in the MPI job have presented their number, +the command returns a[0] to rank 0, a[0]+a[1] to rank 1, +a[0]+a[1]+a[2] to rank 2, and so forth. The -O option enables "+" to +be replaced with other operations. + +Inclusive scans can be useful for assigning a unique index to each +process in the MPI job. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_scan builtin. */ +int +mpi_scan_builtin (list) + WORD_LIST *list; +{ + return reduction_like (list, "mpi_scan", MPI_Scan); +} + +$BUILTIN mpi_exscan +$FUNCTION mpi_exscan_builtin +$SHORT_DOC mpi_exscan number name +Perform an exclusive scan across all processes in the same MPI job. + + -O OPERATION Operation to perform. Must be one of "max", "min", + "sum", "prod", "land", "band", "lor", "bor", "lxor", + "bxor", "maxloc", or "minloc" (default: "sum"). + +Arguments: + NUMBER Integer to use in the scan operation. + + NAME Array variable in which to receive the result and, in + the case of maxloc and minloc, the associated rank. + +In a exclusive-scan operation, each process i presents a number, a[i]. +Once all processes in the MPI job have presented their number, the +command assigns a[0] to NAME on rank 1, a[0]+a[1] to NAME on rank 2, +a[0]+a[1]+a[2] to NAME on rank 3, and so forth. No assignment is +performed on rank 0. The -O option enables "+" to be replaced with +other operations. + +Exclusive scans can be useful for assigning a unique index to each +process in the MPI job. + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_exscan builtin. */ +int +mpi_exscan_builtin (list) + WORD_LIST *list; +{ + return reduction_like (list, "mpi_exscan", MPI_Exscan); +} + +$BUILTIN mpi_allreduce +$FUNCTION mpi_allreduce_builtin +$SHORT_DOC mpi_allreduce number name +Reduce numbers from all processes in an MPI job to a single number. + +Options: + + -O OPERATION Operation to perform. Must be one of "max", "min", + "sum", "prod", "land", "band", "lor", "bor", "lxor", + "bxor", "maxloc", or "minloc" (default: "sum"). + +Arguments: + NUMBER Integer to use in the allreduce operation. + + NAME Array variable in which to receive the result and, in + the case of maxloc and minloc, the associated rank. + +In an all-reduce operation, each process i presents a number, a[i]. +Once all processes in the MPI job have presented their number, the +command returns a[0]+a[1]+...+a[n-1] to all ranks. The -O option +enables "+" to be replaced with other operations. + +All-reduces can be useful for reaching global agreement (e.g., of a +termination condition). + +Exit Status: +Returns 0 unless an invalid option is given or an error occurs. +$END + +/* Here is the mpi_allreduce builtin. */ +int +mpi_allreduce_builtin (list) + WORD_LIST *list; +{ + return reduction_like (list, "mpi_allreduce", MPI_Allreduce); +} diff -Naur bash-4.3/config.h.in mpibash-4.3/config.h.in --- bash-4.3/config.h.in 2013-06-29 15:35:33.000000000 -0600 +++ mpibash-4.3/config.h.in 2014-05-13 11:27:37.314100671 -0600 @@ -1147,6 +1147,12 @@ /* Define if you have the `__argz_stringify' function. */ #undef HAVE___ARGZ_STRINGIFY +/* Define if you have both the header file and the libcircle library. */ +#undef HAVE_LIBCIRCLE + +/* Define if you have the `CIRCLE_cb_reduce_op' function. */ +#undef HAVE_CIRCLE_CB_REDUCE_OP + /* End additions for lib/intl */ #include "config-bot.h" diff -Naur bash-4.3/configure.ac mpibash-4.3/configure.ac --- bash-4.3/configure.ac 2014-02-11 08:37:53.000000000 -0700 +++ mpibash-4.3/configure.ac 2014-05-13 11:27:37.302100179 -0600 @@ -24,7 +24,7 @@ AC_REVISION([for Bash 4.3, version 4.063])dnl define(bashvers, 4.3) -define(relstatus, release) +define(relstatus, MPI) AC_INIT([bash], bashvers-relstatus, [bug-bash@gnu.org]) @@ -813,6 +813,21 @@ fi ]) +dnl Ensure that we can find an MPI library. +AC_CHECK_FUNCS([MPI_Init], [], [ + AC_MSG_ERROR([Cannot continue without MPI. Consider specifying CC=mpicc.])]) + +dnl If we have Libcircle, use it, too. +AC_SEARCH_LIBS([CIRCLE_cb_create], [circle], [AC_CHECK_HEADERS([libcircle.h])]) +if test "x$ac_cv_header_libcircle_h" = xyes; then + libcircle_make_prefix="" + AC_DEFINE([HAVE_LIBCIRCLE], [1], [Define if you have the Libcircle header and library.]) + AC_CHECK_FUNCS([CIRCLE_cb_reduce_op]) +else + libcircle_make_prefix="#" +fi +AC_SUBST([CIRCLE], [$libcircle_make_prefix]) + BASH_CHECK_DECL(strtoimax) BASH_CHECK_DECL(strtol) BASH_CHECK_DECL(strtoll) diff -Naur bash-4.3/Makefile.in mpibash-4.3/Makefile.in --- bash-4.3/Makefile.in 2014-01-25 14:27:30.000000000 -0700 +++ mpibash-4.3/Makefile.in 2014-05-13 11:27:37.314100671 -0600 @@ -104,7 +104,7 @@ VERSPROG = bashversion$(EXEEXT) VERSOBJ = bashversion.$(OBJEXT) -Program = bash$(EXEEXT) +Program = mpibash$(EXEEXT) Version = @BASHVERS@ PatchLevel = `$(BUILD_DIR)/$(VERSPROG) -p` RELSTATUS = @RELSTATUS@ diff -Naur bash-4.3/shell.c mpibash-4.3/shell.c --- bash-4.3/shell.c 2014-01-14 06:04:32.000000000 -0700 +++ mpibash-4.3/shell.c 2014-05-13 11:27:37.314100671 -0600 @@ -107,6 +107,13 @@ extern char *primary_prompt, *secondary_prompt; extern char *this_command_name; +extern void initialize_mpi __P((int, char **)); +extern void finalize_mpi __P((void)); +#ifdef HAVE_LIBCIRCLE +extern void initialize_libcircle __P((int, char **)); +extern void finalize_libcircle __P((void)); +#endif + /* Non-zero means that this shell has already been run; i.e. you should call shell_reinitialize () if you need to start afresh. */ int shell_initialized = 0; @@ -324,7 +331,7 @@ static void init_interactive_script __P((void)); static void set_shell_name __P((char *)); -static void shell_initialize __P((void)); +static void shell_initialize __P((int, char **)); static void shell_reinitialize __P((void)); static void show_shell_usage __P((FILE *, int)); @@ -561,7 +568,7 @@ /* From here on in, the shell must be a normal functioning shell. Variables from the environment are expected to be set, etc. */ - shell_initialize (); + shell_initialize (argc, argv); set_default_lang (); set_default_locale_vars (); @@ -941,6 +948,12 @@ end_job_control (); #endif /* JOB_CONTROL */ +#ifdef HAVE_LIBCIRCLE + finalize_libcircle (); +#else + finalize_mpi (); +#endif + /* Always return the exit status of the last command to our parent. */ sh_exit (s); } @@ -1691,7 +1704,9 @@ /* Do whatever is necessary to initialize the shell. Put new initializations in here. */ static void -shell_initialize () +shell_initialize (argc, argv) + int argc; + char **argv; { char hostname[256]; @@ -1760,6 +1775,17 @@ initialize_shell_options (privileged_mode||running_setuid); initialize_bashopts (privileged_mode||running_setuid); #endif + + /* Initialize Libcircle and MPI. */ +#ifdef HAVE_LIBCIRCLE + initialize_libcircle (argc, argv); + initialize_mpi (argc, argv); + bind_variable ("libcircle", "yes", 0); +#else + initialize_mpi (argc, argv); + bind_variable ("libcircle", "no", 0); +#endif + bind_variable ("mpibash", "yes", 0); } /* Function called by main () when it appears that the shell has already