|  | /* MPI implementation of GNU Fortran Coarray Library | 
|  | Copyright (C) 2011-2025 Free Software Foundation, Inc. | 
|  | Contributed by Tobias Burnus <burnus@net-b.de> | 
|  |  | 
|  | This file is part of the GNU Fortran Coarray Runtime Library (libcaf). | 
|  |  | 
|  | Libcaf is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | Libcaf is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "libcaf.h" | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h>	/* For memcpy.  */ | 
|  | #include <stdarg.h>	/* For variadic arguments.  */ | 
|  | #include <mpi.h> | 
|  |  | 
|  |  | 
|  | /* Define GFC_CAF_CHECK to enable run-time checking.  */ | 
|  | /* #define GFC_CAF_CHECK  1  */ | 
|  |  | 
|  | typedef void ** mpi_token_t; | 
|  | #define TOKEN(X) ((mpi_token_t) (X)) | 
|  |  | 
|  | static void error_stop (int error) __attribute__ ((noreturn)); | 
|  |  | 
|  | /* Global variables.  */ | 
|  | static int caf_mpi_initialized; | 
|  | static int caf_this_image; | 
|  | static int caf_num_images; | 
|  | static int caf_is_finalized; | 
|  |  | 
|  | caf_static_t *caf_static_list = NULL; | 
|  |  | 
|  |  | 
|  | /* Keep in sync with single.c.  */ | 
|  | static void | 
|  | caf_runtime_error (const char *message, ...) | 
|  | { | 
|  | va_list ap; | 
|  | fprintf (stderr, "Fortran runtime error on image %d: ", caf_this_image); | 
|  | va_start (ap, message); | 
|  | vfprintf (stderr, message, ap); | 
|  | va_end (ap); | 
|  | fprintf (stderr, "\n"); | 
|  |  | 
|  | /* FIXME: Shutdown the Fortran RTL to flush the buffer.  PR 43849.  */ | 
|  | /* FIXME: Do some more effort than just MPI_ABORT.  */ | 
|  | MPI_Abort (MPI_COMM_WORLD, EXIT_FAILURE); | 
|  |  | 
|  | /* Should be unreachable, but to make sure also call exit.  */ | 
|  | exit (EXIT_FAILURE); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Initialize coarray program.  This routine assumes that no other | 
|  | MPI initialization happened before; otherwise MPI_Initialized | 
|  | had to be used.  As the MPI library might modify the command-line | 
|  | arguments, the routine should be called before the run-time | 
|  | libaray is initialized.  */ | 
|  |  | 
|  | void | 
|  | _gfortran_caf_init (int *argc, char ***argv) | 
|  | { | 
|  | if (caf_num_images == 0) | 
|  | { | 
|  | /* caf_mpi_initialized is only true if the main program is | 
|  | not written in Fortran.  */ | 
|  | MPI_Initialized (&caf_mpi_initialized); | 
|  | if (!caf_mpi_initialized) | 
|  | MPI_Init (argc, argv); | 
|  |  | 
|  | MPI_Comm_size (MPI_COMM_WORLD, &caf_num_images); | 
|  | MPI_Comm_rank (MPI_COMM_WORLD, &caf_this_image); | 
|  | caf_this_image++; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Finalize coarray program.   */ | 
|  |  | 
|  | void | 
|  | _gfortran_caf_finalize (void) | 
|  | { | 
|  | while (caf_static_list != NULL) | 
|  | { | 
|  | caf_static_t *tmp = caf_static_list->prev; | 
|  |  | 
|  | free (TOKEN (caf_static_list->token)[caf_this_image-1]); | 
|  | free (TOKEN (caf_static_list->token)); | 
|  | free (caf_static_list); | 
|  | caf_static_list = tmp; | 
|  | } | 
|  |  | 
|  | if (!caf_mpi_initialized) | 
|  | MPI_Finalize (); | 
|  |  | 
|  | caf_is_finalized = 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | _gfortran_caf_this_image (int distance __attribute__ ((unused))) | 
|  | { | 
|  | return caf_this_image; | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | _gfortran_caf_num_images (int distance __attribute__ ((unused)), | 
|  | int failed __attribute__ ((unused))) | 
|  | { | 
|  | return caf_num_images; | 
|  | } | 
|  |  | 
|  |  | 
|  | void * | 
|  | _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token, | 
|  | int *stat, char *errmsg, size_t errmsg_len, | 
|  | int num_alloc_comps __attribute__ ((unused))) | 
|  | { | 
|  | void *local; | 
|  | int err; | 
|  |  | 
|  | if (unlikely (caf_is_finalized)) | 
|  | goto error; | 
|  |  | 
|  | /* Start MPI if not already started.  */ | 
|  | if (caf_num_images == 0) | 
|  | _gfortran_caf_init (NULL, NULL); | 
|  |  | 
|  | /* Token contains only a list of pointers.  */ | 
|  | local = malloc (size); | 
|  | *token = malloc (sizeof (mpi_token_t) * caf_num_images); | 
|  |  | 
|  | if (unlikely (local == NULL || *token == NULL)) | 
|  | goto error; | 
|  |  | 
|  | /* token[img-1] is the address of the token in image "img".  */ | 
|  | err = MPI_Allgather (&local, sizeof (void*), MPI_BYTE, TOKEN (*token), | 
|  | sizeof (void*), MPI_BYTE, MPI_COMM_WORLD); | 
|  |  | 
|  | if (unlikely (err)) | 
|  | { | 
|  | free (local); | 
|  | free (*token); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if (type == CAF_REGTYPE_COARRAY_STATIC) | 
|  | { | 
|  | caf_static_t *tmp = malloc (sizeof (caf_static_t)); | 
|  | tmp->prev  = caf_static_list; | 
|  | tmp->token = *token; | 
|  | caf_static_list = tmp; | 
|  | } | 
|  |  | 
|  | if (stat) | 
|  | *stat = 0; | 
|  |  | 
|  | return local; | 
|  |  | 
|  | error: | 
|  | { | 
|  | char *msg; | 
|  |  | 
|  | if (caf_is_finalized) | 
|  | msg = "Failed to allocate coarray - there are stopped images"; | 
|  | else | 
|  | msg = "Failed to allocate coarray"; | 
|  |  | 
|  | if (stat) | 
|  | { | 
|  | *stat = caf_is_finalized ? STAT_STOPPED_IMAGE : 1; | 
|  | if (errmsg_len > 0) | 
|  | { | 
|  | size_t len = (strlen (msg) > errmsg_len) ? errmsg_len | 
|  | : strlen (msg); | 
|  | memcpy (errmsg, msg, len); | 
|  | if (errmsg_len > len) | 
|  | memset (&errmsg[len], ' ', errmsg_len-len); | 
|  | } | 
|  | } | 
|  | else | 
|  | caf_runtime_error (msg); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | _gfortran_caf_deregister (caf_token_t *token, int *stat, char *errmsg, size_t errmsg_len) | 
|  | { | 
|  | if (unlikely (caf_is_finalized)) | 
|  | { | 
|  | const char msg[] = "Failed to deallocate coarray - " | 
|  | "there are stopped images"; | 
|  | if (stat) | 
|  | { | 
|  | *stat = STAT_STOPPED_IMAGE; | 
|  |  | 
|  | if (errmsg_len > 0) | 
|  | { | 
|  | size_t len = (sizeof (msg) - 1 > errmsg_len) | 
|  | ? errmsg_len : sizeof (msg) - 1; | 
|  | memcpy (errmsg, msg, len); | 
|  | if (errmsg_len > len) | 
|  | memset (&errmsg[len], ' ', errmsg_len-len); | 
|  | } | 
|  | return; | 
|  | } | 
|  | caf_runtime_error (msg); | 
|  | } | 
|  |  | 
|  | _gfortran_caf_sync_all (NULL, NULL, 0); | 
|  |  | 
|  | if (stat) | 
|  | *stat = 0; | 
|  |  | 
|  | free (TOKEN (*token)[caf_this_image-1]); | 
|  | free (*token); | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | _gfortran_caf_sync_all (int *stat, char *errmsg, size_t errmsg_len) | 
|  | { | 
|  | int ierr; | 
|  |  | 
|  | if (unlikely (caf_is_finalized)) | 
|  | ierr = STAT_STOPPED_IMAGE; | 
|  | else | 
|  | ierr = MPI_Barrier (MPI_COMM_WORLD); | 
|  |  | 
|  | if (stat) | 
|  | *stat = ierr; | 
|  |  | 
|  | if (ierr) | 
|  | { | 
|  | char *msg; | 
|  | if (caf_is_finalized) | 
|  | msg = "SYNC ALL failed - there are stopped images"; | 
|  | else | 
|  | msg = "SYNC ALL failed"; | 
|  |  | 
|  | if (errmsg_len > 0) | 
|  | { | 
|  | size_t len = (strlen (msg) > errmsg_len) ? errmsg_len | 
|  | : strlen (msg); | 
|  | memcpy (errmsg, msg, len); | 
|  | if (errmsg_len > len) | 
|  | memset (&errmsg[len], ' ', errmsg_len-len); | 
|  | } | 
|  | else | 
|  | caf_runtime_error (msg); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* SYNC IMAGES. Note: SYNC IMAGES(*) is passed as count == -1 while | 
|  | SYNC IMAGES([]) has count == 0. Note further that SYNC IMAGES(*) | 
|  | is not equivalent to SYNC ALL. */ | 
|  | void | 
|  | _gfortran_caf_sync_images (int count, int images[], int *stat, char *errmsg, | 
|  | size_t errmsg_len) | 
|  | { | 
|  | int ierr; | 
|  | if (count == 0 || (count == 1 && images[0] == caf_this_image)) | 
|  | { | 
|  | if (stat) | 
|  | *stat = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | #ifdef GFC_CAF_CHECK | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < count; i++) | 
|  | if (images[i] < 1 || images[i] > caf_num_images) | 
|  | { | 
|  | fprintf (stderr, "COARRAY ERROR: Invalid image index %d to SYNC " | 
|  | "IMAGES", images[i]); | 
|  | error_stop (1); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* FIXME: SYNC IMAGES with a nontrivial argument cannot easily be | 
|  | mapped to MPI communicators. Thus, exist early with an error message.  */ | 
|  | if (count > 0) | 
|  | { | 
|  | fprintf (stderr, "COARRAY ERROR: SYNC IMAGES not yet implemented"); | 
|  | error_stop (1); | 
|  | } | 
|  |  | 
|  | /* Handle SYNC IMAGES(*).  */ | 
|  | if (unlikely (caf_is_finalized)) | 
|  | ierr = STAT_STOPPED_IMAGE; | 
|  | else | 
|  | ierr = MPI_Barrier (MPI_COMM_WORLD); | 
|  |  | 
|  | if (stat) | 
|  | *stat = ierr; | 
|  |  | 
|  | if (ierr) | 
|  | { | 
|  | char *msg; | 
|  | if (caf_is_finalized) | 
|  | msg = "SYNC IMAGES failed - there are stopped images"; | 
|  | else | 
|  | msg = "SYNC IMAGES failed"; | 
|  |  | 
|  | if (errmsg_len > 0) | 
|  | { | 
|  | size_t len = (strlen (msg) > errmsg_len) ? errmsg_len | 
|  | : strlen (msg); | 
|  | memcpy (errmsg, msg, len); | 
|  | if (errmsg_len > len) | 
|  | memset (&errmsg[len], ' ', errmsg_len-len); | 
|  | } | 
|  | else | 
|  | caf_runtime_error (msg); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ERROR STOP the other images.  */ | 
|  |  | 
|  | static void | 
|  | error_stop (int error) | 
|  | { | 
|  | /* FIXME: Shutdown the Fortran RTL to flush the buffer.  PR 43849.  */ | 
|  | /* FIXME: Do some more effort than just MPI_ABORT.  */ | 
|  | MPI_Abort (MPI_COMM_WORLD, error); | 
|  |  | 
|  | /* Should be unreachable, but to make sure also call exit.  */ | 
|  | exit (error); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ERROR STOP function for string arguments.  */ | 
|  |  | 
|  | void | 
|  | _gfortran_caf_error_stop_str (const char *string, size_t len, bool quiet) | 
|  | { | 
|  | if (!quiet) | 
|  | { | 
|  | fputs ("ERROR STOP ", stderr); | 
|  | while (len--) | 
|  | fputc (*(string++), stderr); | 
|  | fputs ("\n", stderr); | 
|  | } | 
|  | error_stop (1); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ERROR STOP function for numerical arguments.  */ | 
|  |  | 
|  | void | 
|  | _gfortran_caf_error_stop (int error, bool quiet) | 
|  | { | 
|  | if (!quiet) | 
|  | fprintf (stderr, "ERROR STOP %d\n", error); | 
|  | error_stop (error); | 
|  | } |