1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <mpi.h>
4 #include <pmix.h>
5 
6 #define SIZE 20
7 #define POS 10
8 #define INITIAL_VALUE 10
9 
10 static pmix_proc_t myproc;
11 
12 /* this is the event notification function we pass down below
13  * when registering for general events - i.e.,, the default
14  * handler. We don't technically need to register one, but it
15  * is usually good practice to catch any events that occur */
notification_fn(size_t evhdlr_registration_id,pmix_status_t status,const pmix_proc_t * source,pmix_info_t info[],size_t ninfo,pmix_info_t results[],size_t nresults,pmix_event_notification_cbfunc_fn_t cbfunc,void * cbdata)16 static void notification_fn(size_t evhdlr_registration_id,
17                             pmix_status_t status,
18                             const pmix_proc_t *source,
19                             pmix_info_t info[], size_t ninfo,
20                             pmix_info_t results[], size_t nresults,
21                             pmix_event_notification_cbfunc_fn_t cbfunc,
22                             void *cbdata)
23 {
24     /* this example doesn't do anything with default events */
25     fprintf(stderr, "Default event handler called with status %s\n", PMIx_Error_string(status));
26 
27     if (NULL != cbfunc) {
28         cbfunc(PMIX_EVENT_ACTION_COMPLETE, NULL, 0, NULL, NULL, cbdata);
29     }
30 }
31 
32 /* this is an event notification function that we explicitly request
33  * be called when the PMIX_MODEL_DECLARED notification is issued.
34  * We could catch it in the general event notification function and test
35  * the status to see if the status matched, but it often is simpler
36  * to declare a use-specific notification callback point. In this case,
37  * we are asking to know whenever a programming model library is
38  * instantiated */
model_callback(size_t evhdlr_registration_id,pmix_status_t status,const pmix_proc_t * source,pmix_info_t info[],size_t ninfo,pmix_info_t results[],size_t nresults,pmix_event_notification_cbfunc_fn_t cbfunc,void * cbdata)39 static void model_callback(size_t evhdlr_registration_id,
40                            pmix_status_t status,
41                            const pmix_proc_t *source,
42                            pmix_info_t info[], size_t ninfo,
43                            pmix_info_t results[], size_t nresults,
44                            pmix_event_notification_cbfunc_fn_t cbfunc,
45                            void *cbdata)
46 {
47     size_t n;
48 
49     fprintf(stderr, "Model event handler called with status %d(%s)\n", status, PMIx_Error_string(status));
50 
51     /* check to see what model declared itself */
52     for (n=0; n < ninfo; n++) {
53         if (PMIX_STRING == info[n].value.type) {
54             fprintf(stderr, "\t%s:\t%s\n", info[n].key, info[n].value.data.string);
55         }
56     }
57 
58     /* we must NOT tell the event handler state machine that we
59      * are the last step as that will prevent it from notifying
60      * anyone else that might be listening for declarations */
61     if (NULL != cbfunc) {
62         cbfunc(PMIX_SUCCESS, NULL, 0, NULL, NULL, cbdata);
63     }
64 }
65 
66 /* event handler registration is done asynchronously because it
67  * may involve the PMIx server registering with the host RM for
68  * external events. So we provide a callback function that returns
69  * the status of the request (success or an error), plus a numerical index
70  * to the registered event. The index is used later on to deregister
71  * an event handler - if we don't explicitly deregister it, then the
72  * PMIx server will do so when it see us exit */
model_registration_callback(pmix_status_t status,size_t evhandler_ref,void * cbdata)73 static void model_registration_callback(pmix_status_t status,
74                                         size_t evhandler_ref,
75                                         void *cbdata)
76 {
77     volatile int *active = (volatile int*)cbdata;
78 
79     if (PMIX_SUCCESS != status) {
80         fprintf(stderr, "Client %s:%d EVENT HANDLER REGISTRATION FAILED WITH STATUS %d, ref=%lu\n",
81                    myproc.nspace, myproc.rank, status, (unsigned long)evhandler_ref);
82     }
83     *active = status;
84 }
85 
main(int argc,char * argv[])86 int main(int argc, char *argv[])
87 {
88     int i, rank, size, next, prev, tag = 201;
89     int array_size = SIZE;
90     int pos = POS;
91     int *send_array;
92     int *recv_array;
93     pmix_info_t *info;
94     size_t ninfo;
95     pmix_status_t code = PMIX_MODEL_DECLARED;
96     pmix_status_t rc;
97     volatile int active;
98 
99 
100     if (1 < argc) {
101         fprintf(stderr, "Declaring ourselves\n");
102         /* declare ourselves as a non-MPI library prior to MPI_Init */
103         ninfo = 4;
104         PMIX_INFO_CREATE(info, ninfo);
105         PMIX_INFO_LOAD(&info[0], PMIX_PROGRAMMING_MODEL, "EXAMPLE", PMIX_STRING);
106         PMIX_INFO_LOAD(&info[1], PMIX_MODEL_LIBRARY_NAME, "FOOL", PMIX_STRING);
107         PMIX_INFO_LOAD(&info[2], PMIX_MODEL_LIBRARY_VERSION, "1.2.3", PMIX_STRING);
108         PMIX_INFO_LOAD(&info[3], PMIX_THREADING_MODEL, "NONE", PMIX_STRING);
109         if (PMIX_SUCCESS != (rc = PMIx_Init(&myproc, info, ninfo))) {
110             fprintf(stderr, "PMIx Init failed: %s\n", PMIx_Error_string(rc));
111             exit(1);
112         }
113         PMIX_INFO_FREE(info, ninfo);
114 
115         /* register a handler specifically for when models declare */
116         active = -1;
117         ninfo = 1;
118         PMIX_INFO_CREATE(info, ninfo);
119         PMIX_INFO_LOAD(&info[0], PMIX_EVENT_HDLR_NAME, "APP-MODEL", PMIX_STRING);
120         PMIx_Register_event_handler(&code, 1, info, ninfo,
121                                     model_callback, model_registration_callback, (void*)&active);
122         while (-1 == active) {
123             usleep(10);
124         }
125         PMIX_INFO_FREE(info, ninfo);
126         if (0 != active) {
127             exit(active);
128         }
129     }
130 
131     /* initialize the MPI library - it will declare itself */
132     MPI_Init(&argc, &argv);
133     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
134     MPI_Comm_size(MPI_COMM_WORLD, &size);
135     if (argc <= 1) {
136         fprintf(stderr, "Registering handler\n");
137         /* register a handler specifically for when models declare */
138         active = -1;
139         ninfo = 1;
140         PMIX_INFO_CREATE(info, ninfo);
141         PMIX_INFO_LOAD(&info[0], PMIX_EVENT_HDLR_NAME, "APP-MODEL", PMIX_STRING);
142 
143         PMIx_Register_event_handler(&code, 1, info, ninfo,
144                                     model_callback, model_registration_callback, (void*)&active);
145         while (-1 == active) {
146             usleep(10);
147         }
148         PMIX_INFO_FREE(info, ninfo);
149         if (0 != active) {
150             exit(active);
151         }
152     }
153 
154     fprintf(stderr, "Rank %d has cleared MPI_Init\n", rank);
155 
156     next = (rank + 1) % size;
157     prev = (rank + size - 1) % size;
158     send_array = malloc(sizeof(int) * SIZE);
159     recv_array = malloc(sizeof(int) * SIZE);
160 
161     for (i = 0; i < array_size; ++i) {
162         send_array[i] = 17;
163         recv_array[i] = -1;
164     }
165 
166     if (0 == rank) {
167         send_array[pos] = INITIAL_VALUE;
168         MPI_Send(send_array, array_size, MPI_INT, next, tag,
169                  MPI_COMM_WORLD);
170     }
171 
172     /* if we didn't already do it, declare another model now */
173     if (argc <= 1) {
174         fprintf(stderr, "Declaring ourselves\n");
175         /* declare ourselves as a non-MPI library after MPI_Init */
176         ninfo = 4;
177         PMIX_INFO_CREATE(info, ninfo);
178         PMIX_INFO_LOAD(&info[0], PMIX_PROGRAMMING_MODEL, "EXAMPLE", PMIX_STRING);
179         PMIX_INFO_LOAD(&info[1], PMIX_MODEL_LIBRARY_NAME, "FOOL", PMIX_STRING);
180         PMIX_INFO_LOAD(&info[2], PMIX_MODEL_LIBRARY_VERSION, "1.2.3", PMIX_STRING);
181         PMIX_INFO_LOAD(&info[3], PMIX_THREADING_MODEL, "NONE", PMIX_STRING);
182 
183         if (PMIX_SUCCESS != (rc = PMIx_Init(&myproc, info, ninfo))) {
184             fprintf(stderr, "PMIx Init failed: %s\n", PMIx_Error_string(rc));
185             exit(1);
186         }
187         PMIX_INFO_FREE(info, ninfo);
188     }
189 
190     while (1) {
191         recv_array[pos] = -1;
192         MPI_Recv(recv_array, array_size, MPI_INT, prev, tag,
193                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
194         send_array[pos] = recv_array[pos];
195         if (rank == 0) {
196             --send_array[pos];
197         }
198         MPI_Send(send_array, array_size, MPI_INT, next, tag, MPI_COMM_WORLD);
199         if (0 == send_array[pos]) {
200             break;
201         }
202     }
203 
204     if (rank == 0) {
205         MPI_Recv(recv_array, array_size, MPI_INT, prev, tag,
206                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
207     }
208 
209     fprintf(stderr, "Rank %d has completed ring\n", rank);
210     MPI_Barrier(MPI_COMM_WORLD);
211     fprintf(stderr, "Rank %d has completed MPI_Barrier\n", rank);
212 
213     /* decrement the PMIx refcount */
214     PMIx_Finalize(NULL, 0);
215     MPI_Finalize();
216     return 0;
217 }
218