1 /*
2  * Copyright (c) 2014-2017, Siemens AG. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <embb/base/c/base.h>
28 #include <embb/mtapi/c/mtapi.h>
29 
30 #include <embb/base/c/internal/unused.h>
31 
32 #include <embb_mtapi_log.h>
33 #include <mtapi_status_t.h>
34 #include <embb_mtapi_node_t.h>
35 #include <embb_mtapi_group_t.h>
36 #include <embb_mtapi_task_t.h>
37 #include <embb_mtapi_job_t.h>
38 #include <embb_mtapi_scheduler_t.h>
39 #include <embb_mtapi_thread_context_t.h>
40 #include <embb_mtapi_task_context_t.h>
41 #include <embb_mtapi_pool_template-inl.h>
42 
43 
44 mtapi_group_hndl_t MTAPI_GROUP_NONE = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID };
45 
46 
47 /* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */
48 
embb_mtapi_pool_implementation(group)49 embb_mtapi_pool_implementation(group)
50 
51 
52 /* ---- CLASS MEMBERS ------------------------------------------------------ */
53 
54 void embb_mtapi_group_initialize(embb_mtapi_group_t * that) {
55   assert(MTAPI_NULL != that);
56 
57   that->group_id = MTAPI_GROUP_ID_NONE;
58   embb_atomic_init_int(&that->deleted, MTAPI_FALSE);
59   embb_atomic_init_int(&that->num_tasks, 0);
60   embb_mtapi_task_queue_initialize(&that->queue);
61 }
62 
embb_mtapi_group_finalize(embb_mtapi_group_t * that)63 void embb_mtapi_group_finalize(embb_mtapi_group_t * that) {
64   assert(MTAPI_NULL != that);
65 
66   embb_atomic_store_int(&that->deleted, MTAPI_TRUE);
67   embb_atomic_destroy_int(&that->deleted);
68   embb_atomic_store_int(&that->num_tasks, 0);
69   embb_atomic_destroy_int(&that->num_tasks);
70   embb_mtapi_task_queue_finalize(&that->queue);
71 }
72 
73 
74 /* ---- INTERFACE FUNCTIONS ------------------------------------------------ */
75 
mtapi_group_create(MTAPI_IN mtapi_group_id_t group_id,MTAPI_IN mtapi_group_attributes_t * attributes,MTAPI_OUT mtapi_status_t * status)76 mtapi_group_hndl_t mtapi_group_create(
77   MTAPI_IN mtapi_group_id_t group_id,
78   MTAPI_IN mtapi_group_attributes_t* attributes,
79   MTAPI_OUT mtapi_status_t* status) {
80   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
81   embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
82   mtapi_group_hndl_t group_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID };
83   embb_mtapi_group_t* group = NULL;
84 
85   embb_mtapi_log_trace("mtapi_group_create() called\n");
86 
87   if (embb_mtapi_node_is_initialized()) {
88     group = embb_mtapi_group_pool_allocate(node->group_pool);
89     if (MTAPI_NULL != group) {
90       embb_mtapi_group_initialize(group);
91       group->group_id = group_id;
92       if (MTAPI_NULL != attributes) {
93         group->attributes = *attributes;
94         local_status = MTAPI_SUCCESS;
95       } else {
96         mtapi_groupattr_init(&group->attributes, &local_status);
97       }
98       if (MTAPI_SUCCESS == local_status) {
99         group_hndl = group->handle;
100       } else {
101         embb_mtapi_group_finalize(group);
102         embb_mtapi_group_pool_deallocate(node->group_pool, group);
103       }
104     } else {
105       local_status = MTAPI_ERR_GROUP_LIMIT;
106     }
107   } else {
108     embb_mtapi_log_error("mtapi not initialized\n");
109     local_status = MTAPI_ERR_NODE_NOTINIT;
110   }
111 
112   mtapi_status_set(status, local_status);
113   return group_hndl;
114 }
115 
mtapi_group_set_attribute(MTAPI_IN mtapi_group_hndl_t group,MTAPI_IN mtapi_uint_t attribute_num,MTAPI_OUT void * attribute,MTAPI_IN mtapi_size_t attribute_size,MTAPI_OUT mtapi_status_t * status)116 void mtapi_group_set_attribute(
117   MTAPI_IN mtapi_group_hndl_t group,
118   MTAPI_IN mtapi_uint_t attribute_num,
119   MTAPI_OUT void* attribute,
120   MTAPI_IN mtapi_size_t attribute_size,
121   MTAPI_OUT mtapi_status_t* status) {
122   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
123   embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
124   embb_mtapi_group_t* local_group;
125 
126   embb_mtapi_log_trace("mtapi_group_set_attribute() called\n");
127 
128   if (embb_mtapi_node_is_initialized()) {
129     if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) {
130       local_group = embb_mtapi_group_pool_get_storage_for_handle(
131         node->group_pool, group);
132       mtapi_groupattr_set(&local_group->attributes, attribute_num,
133         attribute, attribute_size, &local_status);
134     } else {
135       local_status = MTAPI_ERR_GROUP_INVALID;
136     }
137   } else {
138     embb_mtapi_log_error("mtapi not initialized\n");
139     local_status = MTAPI_ERR_NODE_NOTINIT;
140   }
141 
142   mtapi_status_set(status, local_status);
143 }
144 
mtapi_group_get_attribute(MTAPI_IN mtapi_group_hndl_t group,MTAPI_IN mtapi_uint_t attribute_num,MTAPI_OUT void * attribute,MTAPI_IN mtapi_size_t attribute_size,MTAPI_OUT mtapi_status_t * status)145 void mtapi_group_get_attribute(
146   MTAPI_IN mtapi_group_hndl_t group,
147   MTAPI_IN mtapi_uint_t attribute_num,
148   MTAPI_OUT void* attribute,
149   MTAPI_IN mtapi_size_t attribute_size,
150   MTAPI_OUT mtapi_status_t* status ) {
151   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
152 
153   EMBB_UNUSED(attribute_num);
154   EMBB_UNUSED(attribute_size);
155 
156   embb_mtapi_log_trace("mtapi_group_get_attribute() called\n");
157 
158   if (embb_mtapi_node_is_initialized()) {
159     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
160     if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) {
161       /* the following is not needed for now, since there are no attributes
162 
163       embb_mtapi_group_t* local_group =
164         embb_mtapi_group_pool_get_storage_for_handle(
165           node->group_pool, group); */
166 
167       if (MTAPI_NULL == attribute) {
168         local_status = MTAPI_ERR_PARAMETER;
169       } else {
170         /* switch is not needed for now, since there are no attributes
171         switch (attribute_num) {
172         default: */
173           local_status = MTAPI_ERR_ATTR_NUM;
174         /*  break;
175         }*/
176       }
177     } else {
178       local_status = MTAPI_ERR_GROUP_INVALID;
179     }
180   } else {
181     embb_mtapi_log_error("mtapi not initialized\n");
182     local_status = MTAPI_ERR_NODE_NOTINIT;
183   }
184 
185   mtapi_status_set(status, local_status);
186 }
187 
mtapi_group_wait_all(MTAPI_IN mtapi_group_hndl_t group,MTAPI_IN mtapi_timeout_t timeout,MTAPI_OUT mtapi_status_t * status)188 void mtapi_group_wait_all(
189   MTAPI_IN mtapi_group_hndl_t group,
190   MTAPI_IN mtapi_timeout_t timeout,
191   MTAPI_OUT mtapi_status_t* status) {
192   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
193 
194   embb_mtapi_log_trace("mtapi_group_wait_all() called\n");
195 
196   if (embb_mtapi_node_is_initialized()) {
197     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
198     if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) {
199       embb_mtapi_thread_context_t * context = NULL;
200       embb_mtapi_group_t* local_group =
201         embb_mtapi_group_pool_get_storage_for_handle(
202           node->group_pool, group);
203 
204       embb_duration_t wait_duration;
205       embb_time_t start_time;
206       embb_time_t end_time;
207       if (MTAPI_INFINITE < timeout) {
208         embb_duration_set_milliseconds(
209           &wait_duration, (unsigned long long)timeout);
210         embb_time_now(&start_time);
211         embb_time_in(&end_time, &wait_duration);
212       }
213 
214       /* find out on which thread we are */
215       context = embb_mtapi_scheduler_get_current_thread_context(
216         node->scheduler);
217 
218       /* wait for all tasks to arrive in the queue */
219       local_status = MTAPI_SUCCESS;
220       while (embb_atomic_load_int(&local_group->num_tasks)) {
221         embb_mtapi_task_t* local_task;
222 
223         if (MTAPI_INFINITE < timeout) {
224           embb_time_t current_time;
225           embb_time_now(&current_time);
226           if (embb_time_compare(&current_time, &start_time) < 0) {
227             /* time has moved backwards, maybe a wraparound or jitter
228                move end_time backward to avoid endeless loop */
229             start_time = current_time;
230             embb_time_in(&end_time, &wait_duration);
231           }
232           if (embb_time_compare(&current_time, &end_time) > 0) {
233             /* timeout! */
234             local_status = MTAPI_TIMEOUT;
235             break;
236           }
237         }
238 
239         /* fetch and delete all available tasks */
240         local_task = embb_mtapi_task_queue_pop_front(&local_group->queue);
241         while (MTAPI_NULL != local_task) {
242           if (MTAPI_SUCCESS != local_task->error_code) {
243             local_status = local_task->error_code;
244           }
245           embb_mtapi_task_delete(local_task, node->task_pool);
246           embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1);
247 
248           local_task = embb_mtapi_task_queue_pop_front(&local_group->queue);
249         }
250 
251         /* do other work if applicable */
252         embb_mtapi_scheduler_execute_task_or_yield(
253           node->scheduler,
254           node,
255           context);
256       }
257       if (MTAPI_TIMEOUT != local_status) {
258         /* group becomes invalid, so delete it */
259         mtapi_group_delete(group, MTAPI_NULL);
260       }
261     } else {
262       local_status = MTAPI_ERR_GROUP_INVALID;
263     }
264   } else {
265     embb_mtapi_log_error("mtapi not initialized\n");
266     local_status = MTAPI_ERR_NODE_NOTINIT;
267   }
268 
269   mtapi_status_set(status, local_status);
270   embb_mtapi_log_trace("mtapi_group_wait_all() returns\n");
271 }
272 
mtapi_group_wait_any(MTAPI_IN mtapi_group_hndl_t group,MTAPI_OUT void ** result,MTAPI_IN mtapi_timeout_t timeout,MTAPI_OUT mtapi_status_t * status)273 void mtapi_group_wait_any(
274   MTAPI_IN mtapi_group_hndl_t group,
275   MTAPI_OUT void** result,
276   MTAPI_IN mtapi_timeout_t timeout,
277   MTAPI_OUT mtapi_status_t* status) {
278   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
279   void* local_result = MTAPI_NULL;
280 
281   embb_mtapi_log_trace("mtapi_group_wait_any() called\n");
282 
283   if (embb_mtapi_node_is_initialized()) {
284     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
285     if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) {
286       embb_mtapi_group_t* local_group =
287         embb_mtapi_group_pool_get_storage_for_handle(
288           node->group_pool, group);
289 
290       embb_mtapi_task_t* local_task;
291       /* are there any tasks left? */
292       if (0 == embb_atomic_load_int(&local_group->num_tasks)) {
293         /* group becomes invalid, so delete it */
294         mtapi_group_delete(group, &local_status);
295         local_status = MTAPI_GROUP_COMPLETED;
296       } else {
297         embb_mtapi_thread_context_t * context = NULL;
298 
299         embb_duration_t wait_duration;
300         embb_time_t start_time;
301         embb_time_t end_time;
302         if (MTAPI_INFINITE < timeout) {
303           embb_duration_set_milliseconds(
304             &wait_duration, (unsigned long long)timeout);
305           embb_time_now(&start_time);
306           embb_time_in(&end_time, &wait_duration);
307         }
308 
309         /* find out on which thread we are */
310         context = embb_mtapi_scheduler_get_current_thread_context(
311           node->scheduler);
312 
313         /* wait for any task to arrive */
314         local_status = MTAPI_SUCCESS;
315         local_task = embb_mtapi_task_queue_pop_front(&local_group->queue);
316         while (MTAPI_NULL == local_task) {
317           if (MTAPI_INFINITE < timeout) {
318             embb_time_t current_time;
319             embb_time_now(&current_time);
320             if (embb_time_compare(&current_time, &start_time) < 0) {
321               /* time has moved backwards, maybe a wraparound or jitter
322                  move end_time backward to avoid endeless loop */
323               start_time = current_time;
324               embb_time_in(&end_time, &wait_duration);
325             }
326             if (embb_time_compare(&current_time, &end_time) > 0) {
327               /* timeout! */
328               local_status = MTAPI_TIMEOUT;
329               break;
330             }
331           }
332 
333           /* do other work if applicable */
334           embb_mtapi_scheduler_execute_task_or_yield(
335             node->scheduler,
336             node,
337             context);
338 
339           /* try to pop a task from the group queue */
340           local_task = embb_mtapi_task_queue_pop_front(&local_group->queue);
341         }
342         /* was there a timeout, or is there a result? */
343         if (MTAPI_NULL != local_task) {
344           local_result = local_task->result_buffer;
345 
346           /* return error code set by the task */
347           local_status = local_task->error_code;
348 
349           /* delete task */
350           embb_mtapi_task_delete(local_task, node->task_pool);
351           embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1);
352         }
353       }
354     } else {
355       local_status = MTAPI_ERR_GROUP_INVALID;
356     }
357   } else {
358     embb_mtapi_log_error("mtapi not initialized\n");
359     local_status = MTAPI_ERR_NODE_NOTINIT;
360   }
361 
362   /* store result */
363   if (MTAPI_NULL != result) {
364       *result = local_result;
365   }
366 
367   mtapi_status_set(status, local_status);
368   embb_mtapi_log_trace("mtapi_group_wait_any() returns\n");
369 }
370 
mtapi_group_delete(MTAPI_IN mtapi_group_hndl_t group,MTAPI_OUT mtapi_status_t * status)371 void mtapi_group_delete(
372   MTAPI_IN mtapi_group_hndl_t group,
373   MTAPI_OUT mtapi_status_t* status) {
374   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
375 
376   if (embb_mtapi_node_is_initialized()) {
377     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
378     if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) {
379       embb_mtapi_group_t* local_group =
380         embb_mtapi_group_pool_get_storage_for_handle(
381           node->group_pool, group);
382 
383       if (embb_atomic_load_int(&local_group->deleted)) {
384         local_status = MTAPI_ERR_GROUP_INVALID;
385       } else {
386         embb_mtapi_group_finalize(local_group);
387         embb_mtapi_group_pool_deallocate(node->group_pool, local_group);
388         local_status = MTAPI_SUCCESS;
389       }
390     } else {
391       local_status = MTAPI_ERR_GROUP_INVALID;
392     }
393   } else {
394     local_status = MTAPI_ERR_NODE_NOTINIT;
395   }
396 
397   mtapi_status_set(status, local_status);
398 }
399