1 /* MPI implementation of GNU Fortran Coarray Library
2    Copyright (C) 2011-2020 Free Software Foundation, Inc.
3    Contributed by Tobias Burnus <burnus@net-b.de>
4 
5 This file is part of the GNU Fortran Coarray Runtime Library (libcaf).
6 
7 Libcaf is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 Libcaf is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
20 
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 <http://www.gnu.org/licenses/>.  */
25 
26 #include "libcaf.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>	/* For memcpy.  */
30 #include <stdarg.h>	/* For variadic arguments.  */
31 #include <mpi.h>
32 
33 
34 /* Define GFC_CAF_CHECK to enable run-time checking.  */
35 /* #define GFC_CAF_CHECK  1  */
36 
37 typedef void ** mpi_token_t;
38 #define TOKEN(X) ((mpi_token_t) (X))
39 
40 static void error_stop (int error) __attribute__ ((noreturn));
41 
42 /* Global variables.  */
43 static int caf_mpi_initialized;
44 static int caf_this_image;
45 static int caf_num_images;
46 static int caf_is_finalized;
47 
48 caf_static_t *caf_static_list = NULL;
49 
50 
51 /* Keep in sync with single.c.  */
52 static void
caf_runtime_error(const char * message,...)53 caf_runtime_error (const char *message, ...)
54 {
55   va_list ap;
56   fprintf (stderr, "Fortran runtime error on image %d: ", caf_this_image);
57   va_start (ap, message);
58   vfprintf (stderr, message, ap);
59   va_end (ap);
60   fprintf (stderr, "\n");
61 
62   /* FIXME: Shutdown the Fortran RTL to flush the buffer.  PR 43849.  */
63   /* FIXME: Do some more effort than just MPI_ABORT.  */
64   MPI_Abort (MPI_COMM_WORLD, EXIT_FAILURE);
65 
66   /* Should be unreachable, but to make sure also call exit.  */
67   exit (EXIT_FAILURE);
68 }
69 
70 
71 /* Initialize coarray program.  This routine assumes that no other
72    MPI initialization happened before; otherwise MPI_Initialized
73    had to be used.  As the MPI library might modify the command-line
74    arguments, the routine should be called before the run-time
75    libaray is initialized.  */
76 
77 void
_gfortran_caf_init(int * argc,char *** argv)78 _gfortran_caf_init (int *argc, char ***argv)
79 {
80   if (caf_num_images == 0)
81     {
82       /* caf_mpi_initialized is only true if the main program is
83        not written in Fortran.  */
84       MPI_Initialized (&caf_mpi_initialized);
85       if (!caf_mpi_initialized)
86 	MPI_Init (argc, argv);
87 
88       MPI_Comm_size (MPI_COMM_WORLD, &caf_num_images);
89       MPI_Comm_rank (MPI_COMM_WORLD, &caf_this_image);
90       caf_this_image++;
91     }
92 }
93 
94 
95 /* Finalize coarray program.   */
96 
97 void
_gfortran_caf_finalize(void)98 _gfortran_caf_finalize (void)
99 {
100   while (caf_static_list != NULL)
101     {
102       caf_static_t *tmp = caf_static_list->prev;
103 
104       free (TOKEN (caf_static_list->token)[caf_this_image-1]);
105       free (TOKEN (caf_static_list->token));
106       free (caf_static_list);
107       caf_static_list = tmp;
108     }
109 
110   if (!caf_mpi_initialized)
111     MPI_Finalize ();
112 
113   caf_is_finalized = 1;
114 }
115 
116 
117 int
_gfortran_caf_this_image(int distance)118 _gfortran_caf_this_image (int distance __attribute__ ((unused)))
119 {
120   return caf_this_image;
121 }
122 
123 
124 int
_gfortran_caf_num_images(int distance,int failed)125 _gfortran_caf_num_images (int distance __attribute__ ((unused)),
126 			  int failed __attribute__ ((unused)))
127 {
128   return caf_num_images;
129 }
130 
131 
132 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)133 _gfortran_caf_register (size_t size, caf_register_t type, caf_token_t *token,
134 			int *stat, char *errmsg, size_t errmsg_len,
135 			int num_alloc_comps __attribute__ ((unused)))
136 {
137   void *local;
138   int err;
139 
140   if (unlikely (caf_is_finalized))
141     goto error;
142 
143   /* Start MPI if not already started.  */
144   if (caf_num_images == 0)
145     _gfortran_caf_init (NULL, NULL);
146 
147   /* Token contains only a list of pointers.  */
148   local = malloc (size);
149   *token = malloc (sizeof (mpi_token_t) * caf_num_images);
150 
151   if (unlikely (local == NULL || *token == NULL))
152     goto error;
153 
154   /* token[img-1] is the address of the token in image "img".  */
155   err = MPI_Allgather (&local, sizeof (void*), MPI_BYTE, TOKEN (*token),
156 		       sizeof (void*), MPI_BYTE, MPI_COMM_WORLD);
157 
158   if (unlikely (err))
159     {
160       free (local);
161       free (*token);
162       goto error;
163     }
164 
165   if (type == CAF_REGTYPE_COARRAY_STATIC)
166     {
167       caf_static_t *tmp = malloc (sizeof (caf_static_t));
168       tmp->prev  = caf_static_list;
169       tmp->token = *token;
170       caf_static_list = tmp;
171     }
172 
173   if (stat)
174     *stat = 0;
175 
176   return local;
177 
178 error:
179   {
180     char *msg;
181 
182     if (caf_is_finalized)
183       msg = "Failed to allocate coarray - there are stopped images";
184     else
185       msg = "Failed to allocate coarray";
186 
187     if (stat)
188       {
189 	*stat = caf_is_finalized ? STAT_STOPPED_IMAGE : 1;
190 	if (errmsg_len > 0)
191 	  {
192 	    size_t len = (strlen (msg) > errmsg_len) ? errmsg_len
193 	      : strlen (msg);
194 	    memcpy (errmsg, msg, len);
195 	    if (errmsg_len > len)
196 	      memset (&errmsg[len], ' ', errmsg_len-len);
197 	  }
198       }
199     else
200       caf_runtime_error (msg);
201   }
202 
203   return NULL;
204 }
205 
206 
207 void
_gfortran_caf_deregister(caf_token_t * token,int * stat,char * errmsg,size_t errmsg_len)208 _gfortran_caf_deregister (caf_token_t *token, int *stat, char *errmsg, size_t errmsg_len)
209 {
210   if (unlikely (caf_is_finalized))
211     {
212       const char msg[] = "Failed to deallocate coarray - "
213 			  "there are stopped images";
214       if (stat)
215 	{
216 	  *stat = STAT_STOPPED_IMAGE;
217 
218 	  if (errmsg_len > 0)
219 	    {
220 	      size_t len = (sizeof (msg) - 1 > errmsg_len)
221 		? errmsg_len : sizeof (msg) - 1;
222 	      memcpy (errmsg, msg, len);
223 	      if (errmsg_len > len)
224 		memset (&errmsg[len], ' ', errmsg_len-len);
225 	    }
226 	  return;
227 	}
228       caf_runtime_error (msg);
229     }
230 
231   _gfortran_caf_sync_all (NULL, NULL, 0);
232 
233   if (stat)
234     *stat = 0;
235 
236   free (TOKEN (*token)[caf_this_image-1]);
237   free (*token);
238 }
239 
240 
241 void
_gfortran_caf_sync_all(int * stat,char * errmsg,size_t errmsg_len)242 _gfortran_caf_sync_all (int *stat, char *errmsg, size_t errmsg_len)
243 {
244   int ierr;
245 
246   if (unlikely (caf_is_finalized))
247     ierr = STAT_STOPPED_IMAGE;
248   else
249     ierr = MPI_Barrier (MPI_COMM_WORLD);
250 
251   if (stat)
252     *stat = ierr;
253 
254   if (ierr)
255     {
256       char *msg;
257       if (caf_is_finalized)
258 	msg = "SYNC ALL failed - there are stopped images";
259       else
260 	msg = "SYNC ALL failed";
261 
262       if (errmsg_len > 0)
263 	{
264 	  size_t len = (strlen (msg) > errmsg_len) ? errmsg_len
265 	    : strlen (msg);
266 	  memcpy (errmsg, msg, len);
267 	  if (errmsg_len > len)
268 	    memset (&errmsg[len], ' ', errmsg_len-len);
269 	}
270       else
271 	caf_runtime_error (msg);
272     }
273 }
274 
275 
276 /* SYNC IMAGES. Note: SYNC IMAGES(*) is passed as count == -1 while
277    SYNC IMAGES([]) has count == 0. Note further that SYNC IMAGES(*)
278    is not equivalent to SYNC ALL. */
279 void
_gfortran_caf_sync_images(int count,int images[],int * stat,char * errmsg,size_t errmsg_len)280 _gfortran_caf_sync_images (int count, int images[], int *stat, char *errmsg,
281 			   size_t errmsg_len)
282 {
283   int ierr;
284   if (count == 0 || (count == 1 && images[0] == caf_this_image))
285     {
286       if (stat)
287 	*stat = 0;
288       return;
289     }
290 
291 #ifdef GFC_CAF_CHECK
292   {
293     int i;
294 
295     for (i = 0; i < count; i++)
296       if (images[i] < 1 || images[i] > caf_num_images)
297 	{
298 	  fprintf (stderr, "COARRAY ERROR: Invalid image index %d to SYNC "
299 		   "IMAGES", images[i]);
300 	  error_stop (1);
301 	}
302   }
303 #endif
304 
305   /* FIXME: SYNC IMAGES with a nontrivial argument cannot easily be
306      mapped to MPI communicators. Thus, exist early with an error message.  */
307   if (count > 0)
308     {
309       fprintf (stderr, "COARRAY ERROR: SYNC IMAGES not yet implemented");
310       error_stop (1);
311     }
312 
313   /* Handle SYNC IMAGES(*).  */
314   if (unlikely (caf_is_finalized))
315     ierr = STAT_STOPPED_IMAGE;
316   else
317     ierr = MPI_Barrier (MPI_COMM_WORLD);
318 
319   if (stat)
320     *stat = ierr;
321 
322   if (ierr)
323     {
324       char *msg;
325       if (caf_is_finalized)
326 	msg = "SYNC IMAGES failed - there are stopped images";
327       else
328 	msg = "SYNC IMAGES failed";
329 
330       if (errmsg_len > 0)
331 	{
332 	  size_t len = (strlen (msg) > errmsg_len) ? errmsg_len
333 	    : strlen (msg);
334 	  memcpy (errmsg, msg, len);
335 	  if (errmsg_len > len)
336 	    memset (&errmsg[len], ' ', errmsg_len-len);
337 	}
338       else
339 	caf_runtime_error (msg);
340     }
341 }
342 
343 
344 /* ERROR STOP the other images.  */
345 
346 static void
error_stop(int error)347 error_stop (int error)
348 {
349   /* FIXME: Shutdown the Fortran RTL to flush the buffer.  PR 43849.  */
350   /* FIXME: Do some more effort than just MPI_ABORT.  */
351   MPI_Abort (MPI_COMM_WORLD, error);
352 
353   /* Should be unreachable, but to make sure also call exit.  */
354   exit (error);
355 }
356 
357 
358 /* ERROR STOP function for string arguments.  */
359 
360 void
_gfortran_caf_error_stop_str(const char * string,size_t len,bool quiet)361 _gfortran_caf_error_stop_str (const char *string, size_t len, bool quiet)
362 {
363   if (!quiet)
364     {
365       fputs ("ERROR STOP ", stderr);
366       while (len--)
367 	fputc (*(string++), stderr);
368       fputs ("\n", stderr);
369     }
370   error_stop (1);
371 }
372 
373 
374 /* ERROR STOP function for numerical arguments.  */
375 
376 void
_gfortran_caf_error_stop(int error,bool quiet)377 _gfortran_caf_error_stop (int error, bool quiet)
378 {
379   if (!quiet)
380     fprintf (stderr, "ERROR STOP %d\n", error);
381   error_stop (error);
382 }
383