1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2 /*
3  * Copyright (c) 2013      Mellanox Technologies, Inc.
4  *                         All rights reserved.
5  * Copyright (c) 2015      Los Alamos National Security, LLC. All rights
6  *                         reserved.
7  * $COPYRIGHT$
8  *
9  * Additional copyrights may follow
10  *
11  * $HEADER$
12  */
13 /**
14  * @file
15  *
16  * Top-level description of requests
17  */
18 
19 #ifndef OSHMEM_REQUEST_H
20 #define OSHMEM_REQUEST_H
21 
22 #include "oshmem_config.h"
23 #include "oshmem/constants.h"
24 
25 #include "opal/class/opal_free_list.h"
26 #include "opal/class/opal_pointer_array.h"
27 #include "opal/threads/condition.h"
28 
29 BEGIN_C_DECLS
30 
31 /**
32  * Request class
33  */
34 /*OSHMEM_DECLSPEC OBJ_CLASS_DECLARATION(oshmem_request_t);*/
35 OSHMEM_DECLSPEC OBJ_CLASS_DECLARATION(oshmem_request_t);
36 
37 /*
38  * The following include pulls in shared typedefs with debugger plugins.
39  * For more information on why we do this see the Notice to developers
40  * comment at the top of the oshmem_msgq_dll.c file.
41  */
42 
43 #include "request_dbg.h"
44 
45 struct oshmem_request_t;
46 
47 typedef struct oshmem_request_t *SHMEM_Request;
48 typedef struct oshmem_status_public_t SHMEM_Status;
49 
50 /* This constants are used to check status of  request->req_status.SHMEM_ERROR */
51 #define SHMEM_SUCCESS                   0
52 #define SHMEM_ERR_IN_STATUS             18
53 
54 /*
55  * SHMEM_Status
56  */
57 struct oshmem_status_public_t {
58     int SHMEM_SOURCE;
59     /*int MPI_TAG;*/
60     int SHMEM_ERROR;
61     int _count;
62     int _cancelled;
63 };
64 typedef struct oshmem_status_public_t oshmem_status_public_t;
65 
66 typedef int (SHMEM_Grequest_query_function)(void *, SHMEM_Status *);
67 typedef int (SHMEM_Grequest_free_function)(void *);
68 typedef int (SHMEM_Grequest_cancel_function)(void *, int);
69 
70 #define SHMEM_REQUEST_NULL OSHMEM_PREDEFINED_GLOBAL(SHMEM_Request, oshmem_request_null)
71 
72 /*
73  * Required function to free the request and any associated resources.
74  */
75 typedef int (*oshmem_request_free_fn_t)(struct oshmem_request_t** rptr);
76 
77 /*
78  * Optional function to cancel a pending request.
79  */
80 typedef int (*oshmem_request_cancel_fn_t)(struct oshmem_request_t* request,
81                                           int flag);
82 
83 /*
84  * Optional function called when the request is completed from the SHMEM
85  * library perspective. This function is not allowed to release any
86  * ressources related to the request.
87  */
88 typedef int (*oshmem_request_complete_fn_t)(struct oshmem_request_t* request);
89 
90 /* TODO: decide whether to remove comm */
91 /**
92  * Forward declaration
93  */
94 struct oshmem_group_t;
95 
96 /**
97  * Forward declaration
98  */
99 /*struct oshmem_file_t;*/
100 
101 /**
102  * Union for holding several different SHMEM pointer types on the request
103  */
104 typedef union oshmem_shmem_object_t {
105     struct oshmem_group_t *comm;
106 /*    struct oshmem_file_t *file;*/
107 } oshmem_shmem_object_t;
108 
109 /**
110  * Main top-level request struct definition
111  */
112 struct oshmem_request_t {
113     opal_free_list_item_t super; /**< Base type *//*TODO: Implement in shmem */
114     oshmem_request_type_t req_type; /**< Enum indicating the type of the request */
115     oshmem_status_public_t req_status; /**< Completion status */
116     volatile bool req_complete; /**< Flag indicating completion on a request */
117     volatile oshmem_request_state_t req_state; /**< enum indicate the state of the request */
118     bool req_persistent; /* TODO: NOT Required */
119     /**< flag indicating if this is a persistent request */
120     int req_f_to_c_index; /* TODO: NOT Required */
121     /**< Index in Fortran <-> C translation array */
122     oshmem_request_free_fn_t req_free; /**< Called by free */
123     oshmem_request_cancel_fn_t req_cancel; /* TODO: Not Required */
124     /**< Optional function to cancel the request */
125     oshmem_request_complete_fn_t req_complete_cb; /**< Called when the request is SHMEM completed */
126     void *req_complete_cb_data;
127     oshmem_shmem_object_t req_shmem_object; /**< Pointer to SHMEM object that created this request */
128 };
129 
130 /**
131  * Convenience typedef
132  */
133 typedef struct oshmem_request_t oshmem_request_t;
134 
135 /**
136  * Padded struct to maintain back compatibiltiy.
137  * See oshmem/communicator/communicator.h comments with struct oshmem_group_t
138  * for full explanation why we chose the following padding construct for predefines.
139  */
140 #define PREDEFINED_REQUEST_PAD 256
141 
142 struct oshmem_predefined_request_t {
143     struct oshmem_request_t request;
144     char padding[PREDEFINED_REQUEST_PAD - sizeof(oshmem_request_t)];
145 };
146 
147 typedef struct oshmem_predefined_request_t oshmem_predefined_request_t;
148 
149 /**
150  * Initialize a request.  This is a macro to avoid function call
151  * overhead, since this is typically invoked in the critical
152  * performance path (since requests may be re-used, it is possible
153  * that we will have to initialize a request multiple times).
154  */
155 #define OSHMEM_REQUEST_INIT(request, persistent)        \
156     do {                                              \
157         (request)->req_complete = false;              \
158         (request)->req_state = OSHMEM_REQUEST_INACTIVE; \
159         (request)->req_persistent = (persistent);     \
160     } while (0);
161 
162 /**
163  * Finalize a request.  This is a macro to avoid function call
164  * overhead, since this is typically invoked in the critical
165  * performance path (since requests may be re-used, it is possible
166  * that we will have to finalize a request multiple times).
167  *
168  * When finalizing a request, if MPI_Request_f2c() was previously
169  * invoked on that request, then this request was added to the f2c
170  * table, and we need to remove it
171  *
172  * This function should be called only from the SHMEM layer. It should
173  * never be called from the SPML. It take care of the upper level clean-up.
174  * When the user call MPI_Request_free we should release all SHMEM level
175  * ressources, so we have to call this function too.
176  */
177 #define OSHMEM_REQUEST_FINI(request)                                      \
178 do {                                                                    \
179     (request)->req_state = OSHMEM_REQUEST_INVALID;                        \
180     if (SHMEM_UNDEFINED != (request)->req_f_to_c_index) {                 \
181         opal_pointer_array_set_item(&oshmem_request_f_to_c_table,         \
182                                     (request)->req_f_to_c_index, NULL); \
183         (request)->req_f_to_c_index = SHMEM_UNDEFINED;                    \
184     }                                                                   \
185 } while (0);
186 
187 /**
188  * Non-blocking test for request completion.
189  *
190  * @param request (IN)   Array of requests
191  * @param complete (OUT) Flag indicating if index is valid (a request completed).
192  * @param status (OUT)   Status of completed request.
193  * @return               OSHMEM_SUCCESS or failure status.
194  *
195  * Note that upon completion, the request is freed, and the
196  * request handle at index set to NULL.
197  */
198 typedef int (*oshmem_request_test_fn_t)(oshmem_request_t ** rptr,
199                                         int *completed,
200                                         oshmem_status_public_t * status);
201 /**
202  * Non-blocking test for request completion.
203  *
204  * @param count (IN)     Number of requests
205  * @param request (IN)   Array of requests
206  * @param index (OUT)    Index of first completed request.
207  * @param complete (OUT) Flag indicating if index is valid (a request completed).
208  * @param status (OUT)   Status of completed request.
209  * @return               OSHMEM_SUCCESS or failure status.
210  *
211  * Note that upon completion, the request is freed, and the
212  * request handle at index set to NULL.
213  */
214 typedef int (*oshmem_request_test_any_fn_t)(size_t count,
215                                             oshmem_request_t ** requests,
216                                             int *index,
217                                             int *completed,
218                                             oshmem_status_public_t * status);
219 /**
220  * Non-blocking test for request completion.
221  *
222  * @param count (IN)      Number of requests
223  * @param requests (IN)   Array of requests
224  * @param completed (OUT) Flag indicating wether all requests completed.
225  * @param statuses (OUT)  Array of completion statuses.
226  * @return                OSHMEM_SUCCESS or failure status.
227  *
228  * This routine returns completed==true if all requests have completed.
229  * The statuses parameter is only updated if all requests completed. Likewise,
230  * the requests array is not modified (no requests freed), unless all requests
231  * have completed.
232  */
233 typedef int (*oshmem_request_test_all_fn_t)(size_t count,
234                                             oshmem_request_t ** requests,
235                                             int *completed,
236                                             oshmem_status_public_t * statuses);
237 /**
238  * Non-blocking test for some of N requests to complete.
239  *
240  * @param count (IN)        Number of requests
241  * @param requests (INOUT)  Array of requests
242  * @param outcount (OUT)    Number of finished requests
243  * @param indices (OUT)     Indices of the finished requests
244  * @param statuses (OUT)    Array of completion statuses.
245  * @return                  OSHMEM_SUCCESS, OSHMEM_ERR_IN_STATUS or failure status.
246  *
247  */
248 typedef int (*oshmem_request_test_some_fn_t)(size_t count,
249                                              oshmem_request_t ** requests,
250                                              int * outcount,
251                                              int * indices,
252                                              oshmem_status_public_t * statuses);
253 /**
254  * Wait (blocking-mode) for one requests to complete.
255  *
256  * @param request (IN)    Pointer to request.
257  * @param status (OUT)    Status of completed request.
258  * @return                OSHMEM_SUCCESS or failure status.
259  *
260  */
261 typedef int (*oshmem_request_wait_fn_t)(oshmem_request_t ** req_ptr,
262                                         oshmem_status_public_t * status);
263 /**
264  * Wait (blocking-mode) for one of N requests to complete.
265  *
266  * @param count (IN)      Number of requests
267  * @param requests (IN)   Array of requests
268  * @param index (OUT)     Index into request array of completed request.
269  * @param status (OUT)    Status of completed request.
270  * @return                OSHMEM_SUCCESS or failure status.
271  *
272  */
273 typedef int (*oshmem_request_wait_any_fn_t)(size_t count,
274                                             oshmem_request_t ** requests,
275                                             int *index,
276                                             oshmem_status_public_t * status);
277 /**
278  * Wait (blocking-mode) for all of N requests to complete.
279  *
280  * @param count (IN)      Number of requests
281  * @param requests (IN)   Array of requests
282  * @param statuses (OUT)  Array of completion statuses.
283  * @return                OSHMEM_SUCCESS or failure status.
284  *
285  */
286 typedef int (*oshmem_request_wait_all_fn_t)(size_t count,
287                                             oshmem_request_t ** requests,
288                                             oshmem_status_public_t * statuses);
289 /**
290  * Wait (blocking-mode) for some of N requests to complete.
291  *
292  * @param count (IN)        Number of requests
293  * @param requests (INOUT)  Array of requests
294  * @param outcount (OUT)    Number of finished requests
295  * @param indices (OUT)     Indices of the finished requests
296  * @param statuses (OUT)    Array of completion statuses.
297  * @return                  OSHMEM_SUCCESS, OSHMEM_ERR_IN_STATUS or failure status.
298  *
299  */
300 typedef int (*oshmem_request_wait_some_fn_t)(size_t count,
301                                              oshmem_request_t ** requests,
302                                              int * outcount,
303                                              int * indices,
304                                              oshmem_status_public_t * statuses);
305 
306 /**
307  * Replaceable request functions
308  */
309 typedef struct oshmem_request_fns_t {
310     oshmem_request_test_fn_t req_test;
311     oshmem_request_test_any_fn_t req_test_any;
312     oshmem_request_test_all_fn_t req_test_all;
313     oshmem_request_test_some_fn_t req_test_some;
314     oshmem_request_wait_fn_t req_wait;
315     oshmem_request_wait_any_fn_t req_wait_any;
316     oshmem_request_wait_all_fn_t req_wait_all;
317     oshmem_request_wait_some_fn_t req_wait_some;
318 } oshmem_request_fns_t;
319 
320 /**
321  * Globals used for tracking requests and request completion.
322  */
323 OSHMEM_DECLSPEC extern opal_pointer_array_t oshmem_request_f_to_c_table;
324 OSHMEM_DECLSPEC extern size_t oshmem_request_waiting;
325 OSHMEM_DECLSPEC extern size_t oshmem_request_completed;
326 OSHMEM_DECLSPEC extern int32_t oshmem_request_poll;
327 OSHMEM_DECLSPEC extern opal_mutex_t oshmem_request_lock;
328 OSHMEM_DECLSPEC extern opal_condition_t oshmem_request_cond;
329 OSHMEM_DECLSPEC extern oshmem_predefined_request_t oshmem_request_null;
330 OSHMEM_DECLSPEC extern oshmem_request_t oshmem_request_empty;
331 OSHMEM_DECLSPEC extern oshmem_status_public_t oshmem_status_empty;
332 OSHMEM_DECLSPEC extern oshmem_request_fns_t oshmem_request_functions;
333 
334 /**
335  * Initialize the OSHMEM_Request subsystem; invoked during SHMEM_INIT.
336  */
337 int oshmem_request_init(void);
338 
339 /**
340  * Free a persistent request to a MPI_PROC_NULL peer (there's no
341  * freelist to put it back to, so we have to actually OBJ_RELEASE it).
342  */
343 OSHMEM_DECLSPEC int oshmem_request_persistent_proc_null_free(oshmem_request_t **request);
344 
345 /**
346  * Shut down the SHMEM_Request subsystem; invoked during SHMEM_FINALIZE.
347  */
348 int oshmem_request_finalize(void);
349 
350 /**
351  * Cancel a pending request.
352  */
oshmem_request_cancel(oshmem_request_t * request)353 static inline int oshmem_request_cancel(oshmem_request_t* request)
354 {
355     if (request->req_cancel != NULL ) {
356         return request->req_cancel(request, true);
357     }
358     return OSHMEM_SUCCESS;
359 }
360 
361 /**
362  * Free a request.
363  *
364  * @param request (INOUT)   Pointer to request.
365  */
oshmem_request_free(oshmem_request_t ** request)366 static inline int oshmem_request_free(oshmem_request_t** request)
367 {
368     return (*request)->req_free(request);
369 }
370 
371 #define oshmem_request_test       (oshmem_request_functions.req_test)
372 #define oshmem_request_test_any   (oshmem_request_functions.req_test_any)
373 #define oshmem_request_test_all   (oshmem_request_functions.req_test_all)
374 #define oshmem_request_test_some  (oshmem_request_functions.req_test_some)
375 #define oshmem_request_wait       (oshmem_request_functions.req_wait)
376 #define oshmem_request_wait_any   (oshmem_request_functions.req_wait_any)
377 #define oshmem_request_wait_all   (oshmem_request_functions.req_wait_all)
378 #define oshmem_request_wait_some  (oshmem_request_functions.req_wait_some)
379 
380 /**
381  * Wait for any completion. It is a caller responsibility to check for
382  * condition and call us again if needed.
383  */
oshmem_request_wait_any_completion(void)384 static inline void oshmem_request_wait_any_completion(void)
385 {
386     OPAL_THREAD_LOCK(&oshmem_request_lock);
387     oshmem_request_waiting++;
388     opal_condition_wait(&oshmem_request_cond, &oshmem_request_lock);
389     oshmem_request_waiting--;
390     OPAL_THREAD_UNLOCK(&oshmem_request_lock);
391 }
392 
393 /**
394  * Wait a particular request for completion
395  */
oshmem_request_wait_completion(oshmem_request_t * req)396 static inline void oshmem_request_wait_completion(oshmem_request_t *req)
397 {
398     if (false == req->req_complete) {
399 #if OPAL_ENABLE_PROGRESS_THREADS
400         if(opal_progress_spin(&req->req_complete)) {
401             return;
402         }
403 #endif
404         OPAL_THREAD_LOCK(&oshmem_request_lock);
405         oshmem_request_waiting++;
406         while (false == req->req_complete) {
407             opal_condition_wait(&oshmem_request_cond, &oshmem_request_lock);
408         }
409         oshmem_request_waiting--;
410         OPAL_THREAD_UNLOCK(&oshmem_request_lock);
411     }
412 }
413 
414 /**
415  *  Signal or mark a request as complete. If with_signal is true this will
416  *  wake any thread pending on the request and oshmem_request_lock should be
417  *  held while calling this function. If with_signal is false, there will
418  *  signal generated, and no lock required. This is a special case when
419  *  the function is called from the critical path for small messages, where
420  *  we know the current execution flow created the request, and is still
421  *  in the _START macro.
422  */
oshmem_request_complete(oshmem_request_t * request,bool with_signal)423 static inline int oshmem_request_complete(oshmem_request_t* request,
424                                           bool with_signal)
425 {
426     if (NULL != request->req_complete_cb) {
427         request->req_complete_cb(request);
428     }
429     oshmem_request_completed++;
430     request->req_complete = true;
431     if (with_signal && oshmem_request_waiting) {
432         /* Broadcast the condition, otherwise if there is already a thread
433          * waiting on another request it can use all signals.
434          */
435         opal_condition_broadcast(&oshmem_request_cond);
436     }
437     return OSHMEM_SUCCESS;
438 }
439 
440 END_C_DECLS
441 
442 #endif
443