
* Update URL parsing regexes and tests * Get rid of no longer used README * Merge py-udunits and py-cf-units * netcdf -> netcdf-c * setup_environment -> setup_*_environment * Fix doc tests * Few last minute fixes * Simplify prefix removal copypasta
1566 lines
46 KiB
Diff
1566 lines
46 KiB
Diff
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 <config.h>
|
|
+
|
|
+#include <stdio.h>
|
|
+#if defined (HAVE_UNISTD_H)
|
|
+# ifdef _MINIX
|
|
+# include <sys/types.h>
|
|
+# endif
|
|
+# include <unistd.h>
|
|
+#endif
|
|
+
|
|
+#include "../bashintl.h"
|
|
+#include "../shell.h"
|
|
+#include "common.h"
|
|
+#include "bashgetopt.h"
|
|
+#include <libcircle.h>
|
|
+
|
|
+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 <config.h>
|
|
+
|
|
+#include <stdio.h>
|
|
+#if defined (HAVE_UNISTD_H)
|
|
+# ifdef _MINIX
|
|
+# include <sys/types.h>
|
|
+# endif
|
|
+# include <unistd.h>
|
|
+#endif
|
|
+
|
|
+#include "../bashintl.h"
|
|
+#include "../shell.h"
|
|
+#include "common.h"
|
|
+#include "bashgetopt.h"
|
|
+#include <mpi.h>
|
|
+
|
|
+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 <libcircle.h> 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
|