1 /*
2  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3  *                         University Research and Technology
4  *                         Corporation.  All rights reserved.
5  * Copyright (c) 2004-2005 The University of Tennessee and The University
6  *                         of Tennessee Research Foundation.  All rights
7  *                         reserved.
8  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9  *                         University of Stuttgart.  All rights reserved.
10  * Copyright (c) 2004-2005 The Regents of the University of California.
11  *                         All rights reserved.
12  * Copyright (c) 2007      Cisco Systems, Inc.  All rights reserved.
13  * Copyright (c) 2017      Research Organization for Information Science
14  *                         and Technology (RIST). All rights reserved.
15  * $COPYRIGHT$
16  *
17  * Additional copyrights may follow
18  *
19  * $HEADER$
20  */
21 
22 /** @file
23  *
24  * Implementation for taking care of the attribute that can hang off a comm,
25  * win or datatype.
26  */
27 
28 #ifndef OMPI_ATTRIBUTE_H
29 #define OMPI_ATTRIBUTE_H
30 
31 #include <string.h>
32 #include "mpi.h"
33 
34 #include "ompi_config.h"
35 #include "ompi/constants.h"
36 #include "opal/class/opal_object.h"
37 #include "opal/class/opal_hash_table.h"
38 
39 #define ATTR_HASH_SIZE 10
40 
41 /*
42  * Flags for keyvals
43  */
44 #define OMPI_KEYVAL_PREDEFINED     0x0001
45 #define OMPI_KEYVAL_F77            0x0002
46 #define OMPI_KEYVAL_F77_INT        0x0004
47 
48 
49 BEGIN_C_DECLS
50 
51 enum ompi_attribute_type_t {
52     UNUSED_ATTR = 0, /**< Make the compilers happy when we have to construct
53                       * an attribute */
54     COMM_ATTR,       /**< The attribute belongs to a comm object. Starts
55                       * with 1 so that we can have it initialized to 0
56                       * using memset in the constructor */
57     TYPE_ATTR,       /**< The attribute belongs to datatype object */
58     WIN_ATTR         /**< The attribute belongs to a win object */
59 };
60 typedef enum ompi_attribute_type_t ompi_attribute_type_t;
61 
62 
63 /* Old-style MPI-1 Fortran function pointer declarations for copy and
64    delete. These will only be used here and not in the front end
65    functions. */
66 
67 typedef void (ompi_fint_copy_attr_function)(MPI_Fint *oldobj,
68                                                     MPI_Fint *keyval,
69                                                     MPI_Fint *extra_state,
70                                                     MPI_Fint *attr_in,
71                                                     MPI_Fint *attr_out,
72                                                     ompi_fortran_logical_t *flag,
73                                                     MPI_Fint *ierr);
74 typedef void (ompi_fint_delete_attr_function)(MPI_Fint *obj,
75                                                       MPI_Fint *keyval,
76                                                       MPI_Fint *attr_in,
77                                                       MPI_Fint *extra_state,
78                                                       MPI_Fint *ierr);
79 
80 /* New-style MPI-2 Fortran function pointer declarations for copy and
81    delete. These will only be used here and not in the front end
82    functions. */
83 
84 typedef void (ompi_aint_copy_attr_function)(MPI_Fint *oldobj,
85                                             MPI_Fint *keyval,
86                                             void *extra_state,
87                                             void *attr_in,
88                                             void *attr_out,
89                                             ompi_fortran_logical_t *flag,
90                                             MPI_Fint *ierr);
91 typedef void (ompi_aint_delete_attr_function)(MPI_Fint *obj,
92                                               MPI_Fint *keyval,
93                                               void *attr_in,
94                                               void *extra_state,
95                                               MPI_Fint *ierr);
96 /*
97  * Internally the copy function for all kinds of MPI objects has one more
98  * argument, the pointer to the new object. Therefore, we can do on the
99  * flight modifications of the new communicator based on attributes stored
100  * on the main communicator.
101  */
102 typedef int (MPI_Comm_internal_copy_attr_function)(MPI_Comm, int, void *,
103                                                    void *, void *, int *,
104                                                    MPI_Comm);
105 typedef int (MPI_Type_internal_copy_attr_function)(MPI_Datatype, int, void *,
106                                                    void *, void *, int *,
107                                                    MPI_Datatype);
108 typedef int (MPI_Win_internal_copy_attr_function)(MPI_Win, int, void *,
109                                                   void *, void *, int *,
110                                                   MPI_Win);
111 
112 typedef void (ompi_attribute_keyval_destructor_fn_t)(int);
113 
114 /* Union to take care of proper casting of the function pointers
115    passed from the front end functions depending on the type. This
116    will avoid casting function pointers to void*  */
117 
118 union ompi_attribute_fn_ptr_union_t {
119     MPI_Comm_delete_attr_function          *attr_communicator_delete_fn;
120     MPI_Type_delete_attr_function          *attr_datatype_delete_fn;
121     MPI_Win_delete_attr_function           *attr_win_delete_fn;
122 
123     MPI_Comm_internal_copy_attr_function   *attr_communicator_copy_fn;
124     MPI_Type_internal_copy_attr_function   *attr_datatype_copy_fn;
125     MPI_Win_internal_copy_attr_function    *attr_win_copy_fn;
126 
127     /* For Fortran old MPI-1 callback functions */
128 
129     ompi_fint_delete_attr_function *attr_fint_delete_fn;
130     ompi_fint_copy_attr_function   *attr_fint_copy_fn;
131 
132     /* For Fortran new MPI-2 callback functions */
133 
134     ompi_aint_delete_attr_function *attr_aint_delete_fn;
135     ompi_aint_copy_attr_function   *attr_aint_copy_fn;
136 };
137 
138 typedef union ompi_attribute_fn_ptr_union_t ompi_attribute_fn_ptr_union_t;
139 
140 
141 /**
142  * Union to help convert between Fortran attributes (which must be
143  * stored by value) and C pointers (which is the back-end storage of
144  * all attributes).
145  */
146 union ompi_attribute_fortran_ptr_t {
147     void *c_ptr;
148     MPI_Fint f_integer;
149     MPI_Aint f_address;
150 };
151 /**
152  * Convenience typedef
153  */
154 typedef union ompi_attribute_fortran_ptr_t ompi_attribute_fortran_ptr_t;
155 
156 struct ompi_attribute_keyval_t {
157     opal_object_t super;
158     ompi_attribute_type_t attr_type; /**< One of COMM/WIN/DTYPE. This
159                                        will be used to cast the
160                                        copy/delete attribute functions
161                                        properly and error checking */
162     int attr_flag; /**< flag field: contains "OMPI_KEYVAL_PREDEFINED",
163                       "OMPI_KEYVAL_F77"  */
164     ompi_attribute_fn_ptr_union_t copy_attr_fn; /**< Copy function for the
165                                              attribute */
166     ompi_attribute_fn_ptr_union_t delete_attr_fn; /**< Delete function for the
167                                                attribute */
168     ompi_attribute_fortran_ptr_t extra_state; /**< Extra state of the attribute */
169     int key; /**< Keep a track of which key this item belongs to, so that
170                 the key can be deleted when this object is destroyed */
171 
172     /** Extra state for bindings to hang data on.  If non-NULL, will be
173         freed by the C base when the keyval is destroyed. */
174     void *bindings_extra_state;
175 };
176 
177 typedef struct ompi_attribute_keyval_t ompi_attribute_keyval_t;
178 
179 
180 /* Functions */
181 
182 
183 
184 /**
185  * Convenient way to initialize the attribute hash table per MPI-Object
186  */
187 
188 static inline
ompi_attr_hash_init(opal_hash_table_t ** hash)189 int ompi_attr_hash_init(opal_hash_table_t **hash)
190 {
191     *hash = OBJ_NEW(opal_hash_table_t);
192     if (NULL == *hash) {
193         fprintf(stderr, "Error while creating the local attribute list\n");
194         return OMPI_ERR_OUT_OF_RESOURCE;
195     }
196     if (OMPI_SUCCESS != opal_hash_table_init(*hash, ATTR_HASH_SIZE)) {
197         return OMPI_ERR_OUT_OF_RESOURCE;
198     }
199 
200     return MPI_SUCCESS;
201 }
202 
203 /**
204  * Initialize the main attribute hash that stores the keyvals and meta data
205  *
206  * @return OMPI return code
207  */
208 
209 int ompi_attr_init(void);
210 
211 /**
212  * Destroy the main attribute hash that stores the keyvals and meta data
213  */
214 
215 int ompi_attr_finalize(void);
216 
217 
218 /**
219  * Create a new key for use by attribute of Comm/Win/Datatype
220  *
221  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
222  * @param copy_attr_fn   Union variable containing the function pointer
223  *                       to be used in order to copy the attribute (IN)
224  * @param delete_attr_fn Function pointer to be used for deleting the
225  *                       attribute (IN)
226  * @param key            The newly created key is returned here (OUT)
227  * @param extra_state    Extra state to hang off/do some special things (IN)
228  * @param flags          Flags for the key -- flags contain OMPI_KEYVAL_F77,
229  *                       OMPI_KEYVAL_PREDEFINED
230  * @param bindings_extra_state Extra state that, if non-NULL, will
231  *                       automatically be free()'ed by the C base when
232  *                       the keyval is destroyed.
233  *
234  * NOTE: I have taken the assumption that user cannot modify/delete
235  * any predefined keys or the attributes attached. To accomplish this,
236  * all MPI* calls will have OMPI_KEYVAL_PREDEFINED set as 0. MPI
237  * implementors who will need to play with the predefined keys and
238  * attributes would call the ompi* functions here and not the MPI*
239  * functions, with OMPI_KEYVAL_PREDEFINED set to 1.
240  * END OF NOTE
241  *
242  * NOTE: For the function pointers, you need to create a variable of the
243  * union type "ompi_attribute_fn_ptr_union_t" and assign the proper field.
244  * to be passed into this function
245  * END OF NOTE
246  *
247  * @return OMPI return code
248 
249  *
250  */
251 
252 OMPI_DECLSPEC int ompi_attr_create_keyval(ompi_attribute_type_t type,
253                                           ompi_attribute_fn_ptr_union_t copy_attr_fn,
254                                           ompi_attribute_fn_ptr_union_t delete_attr_fn,
255                                           int *key, void *extra_state, int flags,
256                                           void *bindings_extra_state);
257 
258 /**
259  * Same as ompi_attr_create_keyval, but extra_state is a Fortran default integer.
260  */
261 
262 OMPI_DECLSPEC int ompi_attr_create_keyval_fint(ompi_attribute_type_t type,
263                                                ompi_attribute_fn_ptr_union_t copy_attr_fn,
264                                                ompi_attribute_fn_ptr_union_t delete_attr_fn,
265                                                int *key, MPI_Fint extra_state, int flags,
266                                                void *bindings_extra_state);
267 
268 /**
269  * Same as ompi_attr_create_keyval, but extra_state is a Fortran address integer.
270  */
271 
272 OMPI_DECLSPEC int ompi_attr_create_keyval_aint(ompi_attribute_type_t type,
273                                                ompi_attribute_fn_ptr_union_t copy_attr_fn,
274                                                ompi_attribute_fn_ptr_union_t delete_attr_fn,
275                                                int *key, MPI_Aint extra_state, int flags,
276                                                void *bindings_extra_state);
277 
278 /**
279  * Free an attribute keyval
280  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
281  * @param key            key, which is set to MPI_KEY_INVALID (IN/OUT)
282  * @return OMPI error code
283  */
284 
285 int ompi_attr_free_keyval(ompi_attribute_type_t type, int *key,
286                           bool predefined);
287 
288 /**
289  * Set an attribute on the comm/win/datatype in a form valid for C.
290  *
291  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
292  * @param object         The actual Comm/Win/Datatype object (IN)
293  * @param attr_hash      The attribute hash table hanging on the object(IN/OUT)
294  * @param key            Key val for the attribute (IN)
295  * @param attribute      The actual attribute pointer (IN)
296  * @param predefined     Whether the key is predefined or not 0/1 (IN)
297  * @return OMPI error code
298  *
299  * If (*attr_hash) == NULL, a new hash will be created and
300  * initialized.
301  *
302  * All four of these functions (ompi_attr_set_c(), ompi_attr_set_int(),
303  * ompi_attr_set_fint(), and ompi_attr_set_aint())
304  * could have been combined into one function that took some kind of
305  * (void*) and an enum to indicate which way to translate the final
306  * representation, but that just seemed to make an already complicated
307  * situation more complicated through yet another layer of
308  * indirection.
309  *
310  * So yes, this is more code, but it's clearer and less error-prone
311  * (read: better) this way.
312  */
313 int ompi_attr_set_c(ompi_attribute_type_t type, void *object,
314                     opal_hash_table_t **attr_hash,
315                     int key, void *attribute, bool predefined);
316 
317 /**
318  * Set an int predefined attribute in a form valid for C.
319  *
320  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
321  * @param object         The actual Comm/Win/Datatype object (IN)
322  * @param attr_hash      The attribute hash table hanging on the object(IN/OUT)
323  * @param key            Key val for the attribute (IN)
324  * @param attribute      The actual attribute value (IN)
325  * @param predefined     Whether the key is predefined or not 0/1 (IN)
326  * @return OMPI error code
327  *
328  * If (*attr_hash) == NULL, a new hash will be created and
329  * initialized.
330  *
331  * All four of these functions (ompi_attr_set_c(), ompi_attr_set_int(),
332  * ompi_attr_set_fint(), and ompi_attr_set_aint())
333  * could have been combined into one function that took some kind of
334  * (void*) and an enum to indicate which way to translate the final
335  * representation, but that just seemed to make an already complicated
336  * situation more complicated through yet another layer of
337  * indirection.
338  *
339  * So yes, this is more code, but it's clearer and less error-prone
340  * (read: better) this way.
341  */
342 int ompi_attr_set_int(ompi_attribute_type_t type, void *object,
343                       opal_hash_table_t **attr_hash,
344                       int key, int attribute, bool predefined);
345 
346 /**
347  * Set an attribute on the comm/win/datatype in a form valid for
348  * Fortran MPI-1.
349  *
350  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
351  * @param object         The actual Comm/Win/Datatype object (IN)
352  * @param attr_hash      The attribute hash table hanging on the object(IN/OUT)
353  * @param key            Key val for the attribute (IN)
354  * @param attribute      The actual attribute pointer (IN)
355  * @param predefined     Whether the key is predefined or not 0/1 (IN)
356  * @return OMPI error code
357  *
358  * If (*attr_hash) == NULL, a new hash will be created and
359  * initialized.
360  *
361  * All four of these functions (ompi_attr_set_c(), ompi_attr_set_int(),
362  * ompi_attr_set_fint(), and ompi_attr_set_aint())
363  * could have been combined into one function that took some kind of
364  * (void*) and an enum to indicate which way to translate the final
365  * representation, but that just seemed to make an already complicated
366  * situation more complicated through yet another layer of
367  * indirection.
368  *
369  * So yes, this is more code, but it's clearer and less error-prone
370  * (read: better) this way.
371  */
372 OMPI_DECLSPEC int ompi_attr_set_fint(ompi_attribute_type_t type, void *object,
373                                      opal_hash_table_t **attr_hash,
374                                      int key, MPI_Fint attribute,
375                                      bool predefined);
376 
377 /**
378  * Set an attribute on the comm/win/datatype in a form valid for
379  * Fortran MPI-2.
380  *
381  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
382  * @param object         The actual Comm/Win/Datatype object (IN)
383  * @param attr_hash      The attribute hash table hanging on the object(IN/OUT)
384  * @param key            Key val for the attribute (IN)
385  * @param attribute      The actual attribute pointer (IN)
386  * @param predefined     Whether the key is predefined or not 0/1 (IN)
387  * @return OMPI error code
388  *
389  * If (*attr_hash) == NULL, a new hash will be created and
390  * initialized.
391  *
392  * All four of these functions (ompi_attr_set_c(), ompi_attr_set_int(),
393  * ompi_attr_set_fint(), and ompi_attr_set_aint())
394  * could have been combined into one function that took some kind of
395  * (void*) and an enum to indicate which way to translate the final
396  * representation, but that just seemed to make an already complicated
397  * situation more complicated through yet another layer of
398  * indirection.
399  *
400  * So yes, this is more code, but it's clearer and less error-prone
401  * (read: better) this way.
402  */
403 OMPI_DECLSPEC int ompi_attr_set_aint(ompi_attribute_type_t type, void *object,
404                                      opal_hash_table_t **attr_hash,
405                                      int key, MPI_Aint attribute,
406                                      bool predefined);
407 
408 /**
409  * Get an attribute on the comm/win/datatype in a form valid for C.
410  *
411  * @param attr_hash      The attribute hash table hanging on the object(IN)
412  * @param key            Key val for the attribute (IN)
413  * @param attribute      The actual attribute pointer (OUT)
414  * @param flag           Flag whether an attribute is associated
415  *                       with the key (OUT)
416  * @return OMPI error code
417  *
418  * All three of these functions (ompi_attr_get_c(),
419  * ompi_attr_get_fint(), and ompi_attr_get_aint())
420  * could have been combined into one function that took some kind of
421  * (void*) and an enum to indicate which way to translate the final
422  * representation, but that just seemed to make an already complicated
423  * situation more complicated through yet another layer of
424  * indirection.
425  *
426  * So yes, this is more code, but it's clearer and less error-prone
427  * (read: better) this way.
428  */
429 
430 int ompi_attr_get_c(opal_hash_table_t *attr_hash, int key,
431                     void **attribute, int *flag);
432 
433 
434 /**
435  * Get an attribute on the comm/win/datatype in a form valid for
436  * Fortran MPI-1.
437  *
438  * @param attr_hash      The attribute hash table hanging on the object(IN)
439  * @param key            Key val for the attribute (IN)
440  * @param attribute      The actual attribute pointer (OUT)
441  * @param flag           Flag whether an attribute is associated
442  *                       with the key (OUT)
443  * @return OMPI error code
444  *
445  * All three of these functions (ompi_attr_get_c(),
446  * ompi_attr_get_fint(), and ompi_attr_get_aint())
447  * could have been combined into one function that took some kind of
448  * (void*) and an enum to indicate which way to translate the final
449  * representation, but that just seemed to make an already complicated
450  * situation more complicated through yet another layer of
451  * indirection.
452  *
453  * So yes, this is more code, but it's clearer and less error-prone
454  * (read: better) this way.
455  */
456 
457     OMPI_DECLSPEC int ompi_attr_get_fint(opal_hash_table_t *attr_hash, int key,
458                                          MPI_Fint *attribute, int *flag);
459 
460 
461 /**
462  * Get an attribute on the comm/win/datatype in a form valid for
463  * Fortran MPI-2.
464  *
465  * @param attr_hash      The attribute hash table hanging on the object(IN)
466  * @param key            Key val for the attribute (IN)
467  * @param attribute      The actual attribute pointer (OUT)
468  * @param flag           Flag whether an attribute is associated
469  *                       with the key (OUT)
470  * @return OMPI error code
471  *
472  * All three of these functions (ompi_attr_get_c(),
473  * ompi_attr_get_fint(), and ompi_attr_get_aint())
474  * could have been combined into one function that took some kind of
475  * (void*) and an enum to indicate which way to translate the final
476  * representation, but that just seemed to make an already complicated
477  * situation more complicated through yet another layer of
478  * indirection.
479  *
480  * So yes, this is more code, but it's clearer and less error-prone
481  * (read: better) this way.
482  */
483 
484 OMPI_DECLSPEC int ompi_attr_get_aint(opal_hash_table_t *attr_hash, int key,
485                                      MPI_Aint *attribute, int *flag);
486 
487 
488 /**
489  * Delete an attribute on the comm/win/datatype
490  * @param type           Type of attribute (COMM/WIN/DTYPE) (IN)
491  * @param object         The actual Comm/Win/Datatype object (IN)
492  * @param attr_hash      The attribute hash table hanging on the object(IN)
493  * @param key            Key val for the attribute (IN)
494  * @param predefined     Whether the key is predefined or not 0/1 (IN)
495  * @return OMPI error code
496  *
497  */
498 
499 int ompi_attr_delete(ompi_attribute_type_t type, void *object,
500                      opal_hash_table_t *attr_hash , int key,
501                      bool predefined);
502 
503 
504 /**
505  * This to be used from functions like MPI_*_DUP in order to copy all
506  * the attributes from the old Comm/Win/Dtype object to a new
507  * object.
508  * @param type         Type of attribute (COMM/WIN/DTYPE) (IN)
509  * @param old_object   The old COMM/WIN/DTYPE object (IN)
510  * @param new_object   The new COMM/WIN/DTYPE object (IN)
511  * @param oldattr_hash The attribute hash table hanging on old object(IN)
512  * @param newattr_hash The attribute hash table hanging on new object(IN)
513  * @return OMPI error code
514  *
515  */
516 
517 int ompi_attr_copy_all(ompi_attribute_type_t type, void *old_object,
518                        void *new_object, opal_hash_table_t *oldattr_hash,
519                        opal_hash_table_t *newattr_hash);
520 
521 
522 /**
523  * This to be used to delete all the attributes from the Comm/Win/Dtype
524  * object in one shot
525  * @param type         Type of attribute (COMM/WIN/DTYPE) (IN)
526  * @param object       The COMM/WIN/DTYPE object (IN)
527  * @param attr_hash    The attribute hash table hanging on the object(IN)
528  * @return OMPI error code
529  *
530  */
531 
532 int ompi_attr_delete_all(ompi_attribute_type_t type, void *object,
533                         opal_hash_table_t *attr_hash);
534 
535 
536 /**
537  * \internal
538  *
539  * Create all the predefined attributes
540  *
541  * @returns OMPI_SUCCESS
542  */
543 int ompi_attr_create_predefined(void);
544 
545 /**
546  * \internal
547  *
548  * Free all the predefined attributes
549  *
550  * @returns OMPI_SUCCESS
551  */
552 int ompi_attr_free_predefined(void);
553 
554 
555 END_C_DECLS
556 
557 #endif /* OMPI_ATTRIBUTE_H */
558