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 <assert.h>
28 
29 #include <embb/mtapi/c/mtapi.h>
30 #include <embb/base/c/duration.h>
31 #include <embb/base/c/time.h>
32 
33 #include <embb_mtapi_job_t.h>
34 #include <embb_mtapi_log.h>
35 #include <mtapi_status_t.h>
36 #include <embb_mtapi_node_t.h>
37 #include <embb_mtapi_action_t.h>
38 #include <embb_mtapi_pool_template-inl.h>
39 #include <embb_mtapi_attr.h>
40 #include <embb_mtapi_scheduler_t.h>
41 #include <embb_mtapi_task_t.h>
42 
43 
44 /* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */
45 
embb_mtapi_pool_implementation(action)46 embb_mtapi_pool_implementation(action)
47 
48 
49 /* ---- CLASS MEMBERS ------------------------------------------------------ */
50 
51 void embb_mtapi_action_initialize(embb_mtapi_action_t* that) {
52   assert(MTAPI_NULL != that);
53 
54   that->action_function = NULL;
55   that->job_id = MTAPI_JOB_ID_INVALID;
56   that->domain_id = MTAPI_DOMAIN_ID_INVALID;
57   that->node_id = MTAPI_NODE_ID_INVALID;
58   that->enabled = MTAPI_FALSE;
59   that->node_local_data = NULL;
60   that->node_local_data_size = 0;
61   that->plugin_data = MTAPI_NULL;
62   embb_atomic_init_int(&that->num_tasks, 0);
63 }
64 
embb_mtapi_action_finalize(embb_mtapi_action_t * that)65 void embb_mtapi_action_finalize(embb_mtapi_action_t* that) {
66   assert(MTAPI_NULL != that);
67 
68   if (that->is_plugin_action) {
69     // TODO(mw): check status
70     that->plugin_action_finalize_function(that->handle, NULL);
71   }
72   that->action_function = NULL;
73   that->job_id = MTAPI_JOB_ID_INVALID;
74   that->domain_id = MTAPI_DOMAIN_ID_INVALID;
75   that->node_id = MTAPI_NODE_ID_INVALID;
76   that->enabled = MTAPI_FALSE;
77   that->node_local_data = NULL;
78   that->node_local_data_size = 0;
79   that->plugin_data = MTAPI_NULL;
80   embb_atomic_destroy_int(&that->num_tasks);
81 }
82 
embb_mtapi_action_delete_visitor(embb_mtapi_task_t * task,void * user_data)83 static mtapi_boolean_t embb_mtapi_action_delete_visitor(
84   embb_mtapi_task_t * task,
85   void * user_data) {
86   embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data;
87 
88   assert(MTAPI_NULL != action);
89   assert(MTAPI_NULL != task);
90 
91   if (
92     task->action.id == action->handle.id &&
93     task->action.tag == action->handle.tag) {
94     /* task is scheduled and needs to be cancelled */
95     embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED);
96     task->error_code = MTAPI_ERR_ACTION_DELETED;
97   }
98 
99   /* do not remove task from queue */
100   return MTAPI_TRUE;
101 }
102 
embb_mtapi_action_disable_visitor(embb_mtapi_task_t * task,void * user_data)103 static mtapi_boolean_t embb_mtapi_action_disable_visitor(
104   embb_mtapi_task_t * task,
105   void * user_data) {
106   embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data;
107 
108   assert(MTAPI_NULL != action);
109   assert(MTAPI_NULL != task);
110 
111   if (
112     task->action.id == action->handle.id &&
113     task->action.tag == action->handle.tag) {
114     /* task is scheduled and needs to be cancelled */
115     embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED);
116     task->error_code = MTAPI_ERR_ACTION_DISABLED;
117   }
118 
119   /* do not remove task from queue */
120   return MTAPI_TRUE;
121 }
122 
123 
124 /* ---- INTERFACE FUNCTIONS ------------------------------------------------ */
125 
mtapi_action_create(MTAPI_IN mtapi_job_id_t job_id,MTAPI_IN mtapi_action_function_t action_function,MTAPI_IN void * node_local_data,MTAPI_IN mtapi_size_t node_local_data_size,MTAPI_IN mtapi_action_attributes_t * attributes,MTAPI_OUT mtapi_status_t * status)126 mtapi_action_hndl_t mtapi_action_create(
127   MTAPI_IN mtapi_job_id_t job_id,
128   MTAPI_IN mtapi_action_function_t action_function,
129   MTAPI_IN void* node_local_data,
130   MTAPI_IN mtapi_size_t node_local_data_size,
131   MTAPI_IN mtapi_action_attributes_t* attributes,
132   MTAPI_OUT mtapi_status_t* status) {
133   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
134   mtapi_action_hndl_t action_handle = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID };
135 
136   embb_mtapi_log_trace("mtapi_action_create() called\n");
137 
138   if (embb_mtapi_node_is_initialized()) {
139     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
140     /* check if job is valid */
141     if (embb_mtapi_job_is_id_valid(node, job_id)) {
142       embb_mtapi_job_t* job = embb_mtapi_job_get_storage_for_id(node, job_id);
143       embb_mtapi_action_t* new_action =
144         embb_mtapi_action_pool_allocate(node->action_pool);
145       if (MTAPI_NULL != new_action) {
146         new_action->domain_id = node->domain_id;
147         new_action->node_id = node->node_id;
148         new_action->job_id = job_id;
149         new_action->node_local_data = node_local_data;
150         new_action->node_local_data_size = node_local_data_size;
151         new_action->enabled = MTAPI_TRUE;
152         new_action->is_plugin_action = MTAPI_FALSE;
153         embb_atomic_init_int(&new_action->num_tasks, 0);
154 
155         new_action->action_function = action_function;
156 
157         /* set defaults if no attributes were given */
158         if (MTAPI_NULL != attributes) {
159           new_action->attributes = *attributes;
160           local_status = MTAPI_SUCCESS;
161         } else {
162           /* use the default */
163           mtapi_actionattr_init(&new_action->attributes, &local_status);
164         }
165 
166         /* check if affinity is sane */
167         if (0 == new_action->attributes.affinity) {
168           local_status = MTAPI_ERR_PARAMETER;
169         }
170 
171         if (MTAPI_SUCCESS == local_status) {
172           action_handle = new_action->handle;
173           embb_mtapi_job_add_action(job, new_action);
174         } else {
175           embb_mtapi_action_finalize(new_action);
176           embb_mtapi_action_pool_deallocate(node->action_pool, new_action);
177         }
178       } else {
179         /* no more space left in action pool */
180         local_status = MTAPI_ERR_ACTION_LIMIT;
181       }
182     } else {
183       local_status = MTAPI_ERR_JOB_INVALID;
184     }
185   } else {
186     embb_mtapi_log_error("mtapi not initialized\n");
187     local_status = MTAPI_ERR_NODE_NOTINIT;
188   }
189 
190   mtapi_status_set(status, local_status);
191   return action_handle;
192 }
193 
194 
mtapi_action_set_attribute(MTAPI_IN mtapi_action_hndl_t action,MTAPI_IN mtapi_uint_t attribute_num,MTAPI_IN void * attribute,MTAPI_IN mtapi_size_t attribute_size,MTAPI_OUT mtapi_status_t * status)195 void mtapi_action_set_attribute(
196   MTAPI_IN mtapi_action_hndl_t action,
197   MTAPI_IN mtapi_uint_t attribute_num,
198   MTAPI_IN void* attribute,
199   MTAPI_IN mtapi_size_t attribute_size,
200   MTAPI_OUT mtapi_status_t* status) {
201   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
202 
203   embb_mtapi_log_trace("mtapi_action_set_attribute() called\n");
204 
205   if (embb_mtapi_node_is_initialized()) {
206     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
207     if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) {
208       embb_mtapi_action_t* local_action =
209         embb_mtapi_action_pool_get_storage_for_handle(
210           node->action_pool, action);
211       mtapi_actionattr_set(
212         &local_action->attributes,
213         attribute_num,
214         attribute,
215         attribute_size,
216         &local_status);
217     } else {
218       local_status = MTAPI_ERR_ACTION_INVALID;
219     }
220   } else {
221     embb_mtapi_log_error("mtapi not initialized\n");
222     local_status = MTAPI_ERR_NODE_NOTINIT;
223   }
224 
225   mtapi_status_set(status, local_status);
226 }
227 
228 
mtapi_action_get_attribute(MTAPI_IN mtapi_action_hndl_t action,MTAPI_IN mtapi_uint_t attribute_num,MTAPI_OUT void * attribute,MTAPI_IN mtapi_size_t attribute_size,MTAPI_OUT mtapi_status_t * status)229 void mtapi_action_get_attribute(
230   MTAPI_IN mtapi_action_hndl_t action,
231   MTAPI_IN mtapi_uint_t attribute_num,
232   MTAPI_OUT void* attribute,
233   MTAPI_IN mtapi_size_t attribute_size,
234   MTAPI_OUT mtapi_status_t* status) {
235   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
236 
237   embb_mtapi_log_trace("mtapi_action_get_attribute() called\n");
238 
239   if (embb_mtapi_node_is_initialized()) {
240     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
241     if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) {
242       embb_mtapi_action_t* local_action =
243         embb_mtapi_action_pool_get_storage_for_handle(
244           node->action_pool, action);
245 
246       if (MTAPI_NULL == attribute) {
247         local_status = MTAPI_ERR_PARAMETER;
248       } else {
249         switch (attribute_num) {
250         case MTAPI_ACTION_GLOBAL:
251           local_status = embb_mtapi_attr_get_mtapi_boolean_t(
252             &local_action->attributes.global, attribute, attribute_size);
253           break;
254 
255         case MTAPI_ACTION_AFFINITY:
256           local_status = embb_mtapi_attr_get_mtapi_affinity_t(
257             &local_action->attributes.affinity, attribute, attribute_size);
258           break;
259 
260         case MTAPI_ACTION_DOMAIN_SHARED:
261           local_status = embb_mtapi_attr_get_mtapi_boolean_t(
262             &local_action->attributes.domain_shared,
263             attribute,
264             attribute_size);
265           break;
266 
267         default:
268           /* attribute unknown */
269           local_status = MTAPI_ERR_ATTR_NUM;
270           break;
271         }
272       }
273     } else {
274       local_status = MTAPI_ERR_ACTION_INVALID;
275     }
276   } else {
277     embb_mtapi_log_error("mtapi not initialized\n");
278     local_status = MTAPI_ERR_NODE_NOTINIT;
279   }
280 
281   mtapi_status_set(status, local_status);
282 }
283 
284 
mtapi_action_delete(MTAPI_IN mtapi_action_hndl_t action,MTAPI_IN mtapi_timeout_t timeout,MTAPI_OUT mtapi_status_t * status)285 void mtapi_action_delete(
286   MTAPI_IN mtapi_action_hndl_t action,
287   MTAPI_IN mtapi_timeout_t timeout,
288   MTAPI_OUT mtapi_status_t* status) {
289   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
290 
291   embb_mtapi_log_trace("mtapi_action_delete() called\n");
292 
293   if (embb_mtapi_node_is_initialized()) {
294     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
295     if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) {
296       embb_mtapi_action_t* local_action =
297         embb_mtapi_action_pool_get_storage_for_handle(
298           node->action_pool, action);
299 
300       embb_mtapi_thread_context_t * context = NULL;
301 
302       embb_duration_t wait_duration;
303       embb_time_t start_time;
304       embb_time_t end_time;
305       if (MTAPI_INFINITE < timeout) {
306         embb_duration_set_milliseconds(
307           &wait_duration, (unsigned long long)timeout);
308         embb_time_now(&start_time);
309         embb_time_in(&end_time, &wait_duration);
310       }
311 
312       /* cancel all tasks */
313       embb_mtapi_scheduler_process_tasks(
314         node->scheduler, embb_mtapi_action_delete_visitor, local_action);
315 
316       /* find out on which thread we are */
317       context = embb_mtapi_scheduler_get_current_thread_context(
318         node->scheduler);
319 
320       local_status = MTAPI_SUCCESS;
321       while (embb_atomic_load_int(&local_action->num_tasks)) {
322         if (MTAPI_INFINITE < timeout) {
323           embb_time_t current_time;
324           embb_time_now(&current_time);
325           if (embb_time_compare(&current_time, &start_time) < 0) {
326             /* time has moved backwards, maybe a wraparound or jitter
327                move end_time backward to avoid endeless loop */
328             start_time = current_time;
329             embb_time_in(&end_time, &wait_duration);
330           }
331           if (embb_time_compare(&current_time, &end_time) > 0) {
332             /* timeout! */
333             local_status = MTAPI_TIMEOUT;
334             break;
335           }
336         }
337 
338         /* do other work if applicable */
339         embb_mtapi_scheduler_execute_task_or_yield(
340           node->scheduler,
341           node,
342           context);
343       }
344 
345       if (MTAPI_SUCCESS == local_status) {
346         /* delete action */
347         if (embb_mtapi_job_is_id_valid(node, local_action->job_id)) {
348           embb_mtapi_job_t* local_job = embb_mtapi_job_get_storage_for_id(
349             node, local_action->job_id);
350           embb_mtapi_job_remove_action(local_job, local_action);
351         }
352         embb_mtapi_action_finalize(local_action);
353         embb_mtapi_action_pool_deallocate(node->action_pool, local_action);
354       }
355     } else {
356       local_status = MTAPI_ERR_ACTION_INVALID;
357     }
358   } else {
359     embb_mtapi_log_error("mtapi not initialized\n");
360     local_status = MTAPI_ERR_NODE_NOTINIT;
361   }
362 
363   mtapi_status_set(status, local_status);
364 }
365 
mtapi_action_disable(MTAPI_IN mtapi_action_hndl_t action,MTAPI_IN mtapi_timeout_t timeout,MTAPI_OUT mtapi_status_t * status)366 void mtapi_action_disable(
367   MTAPI_IN mtapi_action_hndl_t action,
368   MTAPI_IN mtapi_timeout_t timeout,
369   MTAPI_OUT mtapi_status_t* status) {
370   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
371 
372   embb_mtapi_log_trace("mtapi_action_disable() called\n");
373 
374   if (embb_mtapi_node_is_initialized()) {
375     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
376     if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) {
377       embb_mtapi_action_t* local_action =
378         embb_mtapi_action_pool_get_storage_for_handle(
379           node->action_pool, action);
380       local_action->enabled = MTAPI_FALSE;
381 
382       embb_mtapi_thread_context_t * context = NULL;
383 
384       embb_duration_t wait_duration;
385       embb_time_t start_time;
386       embb_time_t end_time;
387       if (MTAPI_INFINITE < timeout) {
388         embb_duration_set_milliseconds(
389           &wait_duration, (unsigned long long)timeout);
390         embb_time_now(&start_time);
391         embb_time_in(&end_time, &wait_duration);
392       }
393 
394       /* cancel all tasks */
395       embb_mtapi_scheduler_process_tasks(
396         node->scheduler, embb_mtapi_action_disable_visitor, local_action);
397 
398       /* find out on which thread we are */
399       context = embb_mtapi_scheduler_get_current_thread_context(
400         node->scheduler);
401 
402       local_status = MTAPI_SUCCESS;
403       while (embb_atomic_load_int(&local_action->num_tasks)) {
404         if (MTAPI_INFINITE < timeout) {
405           embb_time_t current_time;
406           embb_time_now(&current_time);
407           if (embb_time_compare(&current_time, &start_time) < 0) {
408             /* time has moved backwards, maybe a wraparound or jitter
409                move end_time backward to avoid endeless loop */
410             start_time = current_time;
411             embb_time_in(&end_time, &wait_duration);
412           }
413           if (embb_time_compare(&current_time, &end_time) > 0) {
414             /* timeout! */
415             local_status = MTAPI_TIMEOUT;
416             break;
417           }
418         }
419 
420         /* do other work if applicable */
421         embb_mtapi_scheduler_execute_task_or_yield(
422           node->scheduler,
423           node,
424           context);
425       }
426     } else {
427       local_status = MTAPI_ERR_ACTION_INVALID;
428     }
429   } else {
430     embb_mtapi_log_error("mtapi not initialized\n");
431     local_status = MTAPI_ERR_NODE_NOTINIT;
432   }
433 
434   mtapi_status_set(status, local_status);
435 }
436 
mtapi_action_enable(MTAPI_IN mtapi_action_hndl_t action,MTAPI_OUT mtapi_status_t * status)437 void mtapi_action_enable(
438   MTAPI_IN mtapi_action_hndl_t action,
439   MTAPI_OUT mtapi_status_t* status) {
440   mtapi_status_t local_status = MTAPI_ERR_UNKNOWN;
441 
442   embb_mtapi_log_trace("mtapi_action_enable() called\n");
443 
444   if (embb_mtapi_node_is_initialized()) {
445     embb_mtapi_node_t* node = embb_mtapi_node_get_instance();
446     if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) {
447       embb_mtapi_action_t* local_action =
448         embb_mtapi_action_pool_get_storage_for_handle(
449           node->action_pool, action);
450       local_action->enabled = MTAPI_TRUE;
451       local_status = MTAPI_SUCCESS;
452     } else {
453       local_status = MTAPI_ERR_ACTION_INVALID;
454     }
455   } else {
456     embb_mtapi_log_error("mtapi not initialized\n");
457     local_status = MTAPI_ERR_NODE_NOTINIT;
458   }
459 
460   mtapi_status_set(status, local_status);
461 }
462