LCOV - code coverage report
Current view: top level - capi-machine-learning-inference-1.8.5/c/src - ml-api-service-extension.c (source / functions) Coverage Total Hit
Test: ML API 1.8.5-0 platform/core/api/machine-learning#631dc84d7897cf891d276061d07fd0136e5e4915 Lines: 0.0 % 374 0
Test Date: 2024-05-23 14:26:16 Functions: 0.0 % 20 0

            Line data    Source code
       1              : /* SPDX-License-Identifier: Apache-2.0 */
       2              : /**
       3              :  * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
       4              :  *
       5              :  * @file        ml-api-service-extension.c
       6              :  * @date        1 September 2023
       7              :  * @brief       ML service extension C-API.
       8              :  * @see         https://github.com/nnstreamer/api
       9              :  * @author      Jaeyun Jung <jy1210.jung@samsung.com>
      10              :  * @bug         No known bugs except for NYI items
      11              :  */
      12              : 
      13              : #include "ml-api-service-extension.h"
      14              : 
      15              : /**
      16              :  * @brief The time to wait for new input data in message thread, in millisecond.
      17              :  */
      18              : #define DEFAULT_TIMEOUT 200
      19              : 
      20              : /**
      21              :  * @brief The max number of input data in message queue (0 for no limit).
      22              :  */
      23              : #define DEFAULT_MAX_INPUT 5
      24              : 
      25              : /**
      26              :  * @brief Internal enumeration for ml-service extension types.
      27              :  */
      28              : typedef enum
      29              : {
      30              :   ML_EXTENSION_TYPE_UNKNOWN = 0,
      31              :   ML_EXTENSION_TYPE_SINGLE = 1,
      32              :   ML_EXTENSION_TYPE_PIPELINE = 2,
      33              : 
      34              :   ML_EXTENSION_TYPE_MAX
      35              : } ml_extension_type_e;
      36              : 
      37              : /**
      38              :  * @brief Internal enumeration for the node type in pipeline.
      39              :  */
      40              : typedef enum
      41              : {
      42              :   ML_EXTENSION_NODE_TYPE_UNKNOWN = 0,
      43              :   ML_EXTENSION_NODE_TYPE_INPUT = 1,
      44              :   ML_EXTENSION_NODE_TYPE_OUTPUT = 2,
      45              : 
      46              :   ML_EXTENSION_NODE_TYPE_MAX
      47              : } ml_extension_node_type_e;
      48              : 
      49              : /**
      50              :  * @brief Internal structure of the node info in pipeline.
      51              :  */
      52              : typedef struct
      53              : {
      54              :   gchar *name;
      55              :   ml_extension_node_type_e type;
      56              :   ml_tensors_info_h info;
      57              :   void *handle;
      58              :   void *mls;
      59              : } ml_extension_node_info_s;
      60              : 
      61              : /**
      62              :  * @brief Internal structure of the message in ml-service extension handle.
      63              :  */
      64              : typedef struct
      65              : {
      66              :   gchar *name;
      67              :   ml_tensors_data_h input;
      68              :   ml_tensors_data_h output;
      69              : } ml_extension_msg_s;
      70              : 
      71              : /**
      72              :  * @brief Internal structure for ml-service extension handle.
      73              :  */
      74              : typedef struct
      75              : {
      76              :   ml_extension_type_e type;
      77              :   gboolean running;
      78              :   guint timeout; /**< The time to wait for new input data in message thread, in millisecond (see DEFAULT_TIMEOUT). */
      79              :   guint max_input; /**< The max number of input data in message queue (see DEFAULT_MAX_INPUT). */
      80              :   GThread *msg_thread;
      81              :   GAsyncQueue *msg_queue;
      82              : 
      83              :   /**
      84              :    * Handles for each ml-service extension type.
      85              :    * - single : Default. Open model file and prepare invoke. The configuration should include model information.
      86              :    * - pipeline : Construct a pipeline from configuration. The configuration should include pipeline description.
      87              :    */
      88              :   ml_single_h single;
      89              : 
      90              :   ml_pipeline_h pipeline;
      91              :   GHashTable *node_table;
      92              : } ml_extension_s;
      93              : 
      94              : /**
      95              :  * @brief Internal function to create node info in pipeline.
      96              :  */
      97              : static ml_extension_node_info_s *
      98            0 : _ml_extension_node_info_new (ml_service_s * mls, const gchar * name,
      99              :     ml_extension_node_type_e type)
     100              : {
     101            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     102              :   ml_extension_node_info_s *node_info;
     103              : 
     104            0 :   if (!STR_IS_VALID (name)) {
     105            0 :     _ml_error_report_return (NULL,
     106              :         "Cannot add new node info, invalid node name '%s'.", name);
     107              :   }
     108              : 
     109            0 :   if (g_hash_table_lookup (ext->node_table, name)) {
     110            0 :     _ml_error_report_return (NULL,
     111              :         "Cannot add duplicated node '%s' in ml-service pipeline.", name);
     112              :   }
     113              : 
     114            0 :   node_info = g_try_new0 (ml_extension_node_info_s, 1);
     115            0 :   if (!node_info) {
     116            0 :     _ml_error_report_return (NULL,
     117              :         "Failed to allocate new memory for node info in ml-service pipeline. Out of memory?");
     118              :   }
     119              : 
     120            0 :   node_info->name = g_strdup (name);
     121            0 :   node_info->type = type;
     122            0 :   node_info->mls = mls;
     123              : 
     124            0 :   g_hash_table_insert (ext->node_table, g_strdup (name), node_info);
     125              : 
     126            0 :   return node_info;
     127              : }
     128              : 
     129              : /**
     130              :  * @brief Internal function to release pipeline node info.
     131              :  */
     132              : static void
     133            0 : _ml_extension_node_info_free (gpointer data)
     134              : {
     135            0 :   ml_extension_node_info_s *node_info = (ml_extension_node_info_s *) data;
     136              : 
     137            0 :   if (!node_info)
     138            0 :     return;
     139              : 
     140            0 :   if (node_info->info)
     141            0 :     ml_tensors_info_destroy (node_info->info);
     142              : 
     143            0 :   g_free (node_info->name);
     144            0 :   g_free (node_info);
     145              : }
     146              : 
     147              : /**
     148              :  * @brief Internal function to get the node info in ml-service extension.
     149              :  */
     150              : static ml_extension_node_info_s *
     151            0 : _ml_extension_node_info_get (ml_extension_s * ext, const gchar * name)
     152              : {
     153            0 :   if (!STR_IS_VALID (name))
     154            0 :     return NULL;
     155              : 
     156            0 :   return g_hash_table_lookup (ext->node_table, name);
     157              : }
     158              : 
     159              : /**
     160              :  * @brief Internal function to invoke ml-service event for new data.
     161              :  */
     162              : static int
     163            0 : _ml_extension_invoke_event_new_data (ml_service_s * mls, const char *name,
     164              :     const ml_tensors_data_h data)
     165              : {
     166            0 :   ml_service_event_cb_info_s cb_info = { 0 };
     167            0 :   ml_information_h info = NULL;
     168            0 :   int status = ML_ERROR_NONE;
     169              : 
     170            0 :   if (!mls || !data) {
     171            0 :     _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     172              :         "Failed to create ml-service event data, invalid parameter.");
     173              :   }
     174              : 
     175            0 :   _ml_service_get_event_cb_info (mls, &cb_info);
     176              : 
     177            0 :   if (cb_info.cb) {
     178              :     /* Create information handle for ml-service event. */
     179            0 :     status = _ml_information_create (&info);
     180            0 :     if (status != ML_ERROR_NONE)
     181            0 :       goto done;
     182              : 
     183            0 :     if (name) {
     184            0 :       status = _ml_information_set (info, "name", (void *) name, NULL);
     185            0 :       if (status != ML_ERROR_NONE)
     186            0 :         goto done;
     187              :     }
     188              : 
     189            0 :     status = _ml_information_set (info, "data", (void *) data, NULL);
     190            0 :     if (status == ML_ERROR_NONE)
     191            0 :       cb_info.cb (ML_SERVICE_EVENT_NEW_DATA, info, cb_info.pdata);
     192              :   }
     193              : 
     194            0 : done:
     195            0 :   if (info)
     196            0 :     ml_information_destroy (info);
     197              : 
     198            0 :   if (status != ML_ERROR_NONE) {
     199            0 :     _ml_error_report ("Failed to invoke 'new data' event.");
     200              :   }
     201              : 
     202            0 :   return status;
     203              : }
     204              : 
     205              : /**
     206              :  * @brief Internal callback for sink node in pipeline description.
     207              :  */
     208              : static void
     209            0 : _ml_extension_pipeline_sink_cb (const ml_tensors_data_h data,
     210              :     const ml_tensors_info_h info, void *user_data)
     211              : {
     212            0 :   ml_extension_node_info_s *node_info = (ml_extension_node_info_s *) user_data;
     213            0 :   ml_service_s *mls = (ml_service_s *) node_info->mls;
     214              : 
     215            0 :   _ml_extension_invoke_event_new_data (mls, node_info->name, data);
     216            0 : }
     217              : 
     218              : /**
     219              :  * @brief Internal function to release ml-service extension message.
     220              :  */
     221              : static void
     222            0 : _ml_extension_msg_free (gpointer data)
     223              : {
     224            0 :   ml_extension_msg_s *msg = (ml_extension_msg_s *) data;
     225              : 
     226            0 :   if (!msg)
     227            0 :     return;
     228              : 
     229            0 :   if (msg->input)
     230            0 :     ml_tensors_data_destroy (msg->input);
     231            0 :   if (msg->output)
     232            0 :     ml_tensors_data_destroy (msg->output);
     233              : 
     234            0 :   g_free (msg->name);
     235            0 :   g_free (msg);
     236              : }
     237              : 
     238              : /**
     239              :  * @brief Internal function to process ml-service extension message.
     240              :  */
     241              : static gpointer
     242            0 : _ml_extension_msg_thread (gpointer data)
     243              : {
     244            0 :   ml_service_s *mls = (ml_service_s *) data;
     245            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     246              :   int status;
     247              : 
     248            0 :   g_mutex_lock (&mls->lock);
     249            0 :   ext->running = TRUE;
     250            0 :   g_cond_signal (&mls->cond);
     251            0 :   g_mutex_unlock (&mls->lock);
     252              : 
     253            0 :   while (ext->running) {
     254              :     ml_extension_msg_s *msg;
     255              : 
     256            0 :     msg = g_async_queue_timeout_pop (ext->msg_queue,
     257            0 :         ext->timeout * G_TIME_SPAN_MILLISECOND);
     258              : 
     259            0 :     if (msg) {
     260            0 :       switch (ext->type) {
     261            0 :         case ML_EXTENSION_TYPE_SINGLE:
     262              :         {
     263            0 :           status = ml_single_invoke (ext->single, msg->input, &msg->output);
     264              : 
     265            0 :           if (status == ML_ERROR_NONE) {
     266            0 :             _ml_extension_invoke_event_new_data (mls, NULL, msg->output);
     267              :           } else {
     268            0 :             _ml_error_report
     269              :                 ("Failed to invoke the model in ml-service extension thread.");
     270              :           }
     271            0 :           break;
     272              :         }
     273            0 :         case ML_EXTENSION_TYPE_PIPELINE:
     274              :         {
     275              :           ml_extension_node_info_s *node_info;
     276              : 
     277            0 :           node_info = _ml_extension_node_info_get (ext, msg->name);
     278              : 
     279            0 :           if (node_info && node_info->type == ML_EXTENSION_NODE_TYPE_INPUT) {
     280              :             /* The input data will be released in the pipeline. */
     281            0 :             status = ml_pipeline_src_input_data (node_info->handle, msg->input,
     282              :                 ML_PIPELINE_BUF_POLICY_AUTO_FREE);
     283            0 :             msg->input = NULL;
     284              : 
     285            0 :             if (status != ML_ERROR_NONE) {
     286            0 :               _ml_error_report
     287              :                   ("Failed to push input data into the pipeline in ml-service extension thread.");
     288              :             }
     289              :           } else {
     290            0 :             _ml_error_report
     291              :                 ("Failed to push input data into the pipeline, cannot find input node '%s'.",
     292              :                 msg->name);
     293              :           }
     294            0 :           break;
     295              :         }
     296            0 :         default:
     297              :           /* Unknown ml-service extension type, skip this. */
     298            0 :           break;
     299              :       }
     300              : 
     301            0 :       _ml_extension_msg_free (msg);
     302              :     }
     303              :   }
     304              : 
     305            0 :   return NULL;
     306              : }
     307              : 
     308              : /**
     309              :  * @brief Wrapper to release tensors-info handle.
     310              :  */
     311              : static void
     312            0 : _ml_extension_destroy_tensors_info (void *data)
     313              : {
     314            0 :   ml_tensors_info_h info = (ml_tensors_info_h) data;
     315              : 
     316            0 :   if (info)
     317            0 :     ml_tensors_info_destroy (info);
     318            0 : }
     319              : 
     320              : /**
     321              :  * @brief Internal function to parse single-shot info from json.
     322              :  */
     323              : static int
     324            0 : _ml_extension_conf_parse_single (ml_service_s * mls, JsonObject * single)
     325              : {
     326            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     327              :   ml_option_h option;
     328              :   int status;
     329              : 
     330            0 :   status = ml_option_create (&option);
     331            0 :   if (status != ML_ERROR_NONE) {
     332            0 :     _ml_error_report_return (status,
     333              :         "Failed to parse configuration file, cannot create ml-option handle.");
     334              :   }
     335              : 
     336              :   /**
     337              :    * 1. "key" : load model info from ml-service agent.
     338              :    * 2. "model" : configuration file includes model path.
     339              :    */
     340            0 :   if (json_object_has_member (single, "key")) {
     341            0 :     const gchar *key = json_object_get_string_member (single, "key");
     342              : 
     343            0 :     if (STR_IS_VALID (key)) {
     344              :       ml_information_h model_info;
     345              : 
     346            0 :       status = ml_service_model_get_activated (key, &model_info);
     347            0 :       if (status == ML_ERROR_NONE) {
     348            0 :         gchar *paths = NULL;
     349              : 
     350              :         /** @todo parse desc and other information if necessary. */
     351            0 :         ml_information_get (model_info, "path", (void **) (&paths));
     352            0 :         ml_option_set (option, "models", g_strdup (paths), g_free);
     353              : 
     354            0 :         ml_information_destroy (model_info);
     355              :       } else {
     356            0 :         _ml_error_report
     357              :             ("Failed to parse configuration file, cannot get the model of '%s'.",
     358              :             key);
     359            0 :         goto error;
     360              :       }
     361              :     }
     362            0 :   } else if (json_object_has_member (single, "model")) {
     363            0 :     JsonNode *file_node = json_object_get_member (single, "model");
     364            0 :     gchar *paths = NULL;
     365              : 
     366            0 :     status = _ml_service_conf_parse_string (file_node, ",", &paths);
     367            0 :     if (status != ML_ERROR_NONE) {
     368            0 :       _ml_error_report
     369              :           ("Failed to parse configuration file, it should have valid model path.");
     370            0 :       goto error;
     371              :     }
     372              : 
     373            0 :     ml_option_set (option, "models", paths, g_free);
     374              :   } else {
     375            0 :     status = ML_ERROR_INVALID_PARAMETER;
     376            0 :     _ml_error_report
     377              :         ("Failed to parse configuration file, cannot get the model path.");
     378            0 :     goto error;
     379              :   }
     380              : 
     381            0 :   if (json_object_has_member (single, "framework")) {
     382            0 :     const gchar *fw = json_object_get_string_member (single, "framework");
     383              : 
     384            0 :     if (STR_IS_VALID (fw))
     385            0 :       ml_option_set (option, "framework_name", g_strdup (fw), g_free);
     386              :   }
     387              : 
     388            0 :   if (json_object_has_member (single, "input_info")) {
     389            0 :     JsonNode *info_node = json_object_get_member (single, "input_info");
     390              :     ml_tensors_info_h in_info;
     391              : 
     392            0 :     status = _ml_service_conf_parse_tensors_info (info_node, &in_info);
     393            0 :     if (status != ML_ERROR_NONE) {
     394            0 :       _ml_error_report
     395              :           ("Failed to parse configuration file, cannot parse input information.");
     396            0 :       goto error;
     397              :     }
     398              : 
     399            0 :     ml_option_set (option, "input_info", in_info,
     400              :         _ml_extension_destroy_tensors_info);
     401              :   }
     402              : 
     403            0 :   if (json_object_has_member (single, "output_info")) {
     404            0 :     JsonNode *info_node = json_object_get_member (single, "output_info");
     405              :     ml_tensors_info_h out_info;
     406              : 
     407            0 :     status = _ml_service_conf_parse_tensors_info (info_node, &out_info);
     408            0 :     if (status != ML_ERROR_NONE) {
     409            0 :       _ml_error_report
     410              :           ("Failed to parse configuration file, cannot parse output information.");
     411            0 :       goto error;
     412              :     }
     413              : 
     414            0 :     ml_option_set (option, "output_info", out_info,
     415              :         _ml_extension_destroy_tensors_info);
     416              :   }
     417              : 
     418            0 :   if (json_object_has_member (single, "custom")) {
     419            0 :     const gchar *custom = json_object_get_string_member (single, "custom");
     420              : 
     421            0 :     if (STR_IS_VALID (custom))
     422            0 :       ml_option_set (option, "custom", g_strdup (custom), g_free);
     423              :   }
     424              : 
     425            0 : error:
     426            0 :   if (status == ML_ERROR_NONE)
     427            0 :     status = ml_single_open_with_option (&ext->single, option);
     428              : 
     429            0 :   ml_option_destroy (option);
     430            0 :   return status;
     431              : }
     432              : 
     433              : /**
     434              :  * @brief Internal function to parse the node info in pipeline.
     435              :  */
     436              : static int
     437            0 : _ml_extension_conf_parse_pipeline_node (ml_service_s * mls, JsonNode * node,
     438              :     ml_extension_node_type_e type)
     439              : {
     440            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     441            0 :   JsonArray *array = NULL;
     442              :   JsonObject *object;
     443              :   guint i, n;
     444              :   int status;
     445              : 
     446            0 :   n = 1;
     447            0 :   if (JSON_NODE_HOLDS_ARRAY (node)) {
     448            0 :     array = json_node_get_array (node);
     449            0 :     n = json_array_get_length (array);
     450              :   }
     451              : 
     452            0 :   for (i = 0; i < n; i++) {
     453            0 :     const gchar *name = NULL;
     454              :     ml_extension_node_info_s *node_info;
     455              : 
     456            0 :     if (array)
     457            0 :       object = json_array_get_object_element (array, i);
     458              :     else
     459            0 :       object = json_node_get_object (node);
     460              : 
     461            0 :     if (json_object_has_member (object, "name"))
     462            0 :       name = json_object_get_string_member (object, "name");
     463              : 
     464            0 :     node_info = _ml_extension_node_info_new (mls, name, type);
     465            0 :     if (!node_info) {
     466            0 :       _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     467              :           "Failed to parse configuration file, cannot add new node information.");
     468              :     }
     469              : 
     470            0 :     if (json_object_has_member (object, "info")) {
     471            0 :       JsonNode *info_node = json_object_get_member (object, "info");
     472              : 
     473            0 :       status = _ml_service_conf_parse_tensors_info (info_node,
     474              :           &node_info->info);
     475            0 :       if (status != ML_ERROR_NONE) {
     476            0 :         _ml_error_report_return (status,
     477              :             "Failed to parse configuration file, cannot parse the information.");
     478              :       }
     479              :     } else {
     480            0 :       _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     481              :           "Failed to parse configuration file, cannot find node information.");
     482              :     }
     483              : 
     484            0 :     switch (type) {
     485            0 :       case ML_EXTENSION_NODE_TYPE_INPUT:
     486            0 :         status = ml_pipeline_src_get_handle (ext->pipeline, name,
     487            0 :             &node_info->handle);
     488            0 :         break;
     489            0 :       case ML_EXTENSION_NODE_TYPE_OUTPUT:
     490            0 :         status = ml_pipeline_sink_register (ext->pipeline, name,
     491            0 :             _ml_extension_pipeline_sink_cb, node_info, &node_info->handle);
     492            0 :         break;
     493            0 :       default:
     494            0 :         status = ML_ERROR_INVALID_PARAMETER;
     495            0 :         break;
     496              :     }
     497              : 
     498            0 :     if (status != ML_ERROR_NONE) {
     499            0 :       _ml_error_report_return (status,
     500              :           "Failed to parse configuration file, cannot get the handle for pipeline node.");
     501              :     }
     502              :   }
     503              : 
     504            0 :   return ML_ERROR_NONE;
     505              : }
     506              : 
     507              : /**
     508              :  * @brief Internal function to parse pipeline info from json.
     509              :  */
     510              : static int
     511            0 : _ml_extension_conf_parse_pipeline (ml_service_s * mls, JsonObject * pipe)
     512              : {
     513            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     514            0 :   g_autofree gchar *desc = NULL;
     515              :   int status;
     516              : 
     517              :   /**
     518              :    * 1. "key" : load pipeline from ml-service agent.
     519              :    * 2. "description" : configuration file includes pipeline description.
     520              :    */
     521            0 :   if (json_object_has_member (pipe, "key")) {
     522            0 :     const gchar *key = json_object_get_string_member (pipe, "key");
     523              : 
     524            0 :     if (STR_IS_VALID (key)) {
     525            0 :       status = ml_service_pipeline_get (key, &desc);
     526            0 :       if (status != ML_ERROR_NONE) {
     527            0 :         _ml_error_report_return (status,
     528              :             "Failed to parse configuration file, cannot get the pipeline of '%s'.",
     529              :             key);
     530              :       }
     531              :     }
     532            0 :   } else if (json_object_has_member (pipe, "description")) {
     533            0 :     desc = g_strdup (json_object_get_string_member (pipe, "description"));
     534              :   } else {
     535            0 :     _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     536              :         "Failed to parse configuration file, cannot get the pipeline description.");
     537              :   }
     538              : 
     539            0 :   status = ml_pipeline_construct (desc, NULL, NULL, &ext->pipeline);
     540            0 :   if (status != ML_ERROR_NONE) {
     541            0 :     _ml_error_report_return (status,
     542              :         "Failed to parse configuration file, cannot construct the pipeline.");
     543              :   }
     544              : 
     545            0 :   if (json_object_has_member (pipe, "input_node")) {
     546            0 :     JsonNode *node = json_object_get_member (pipe, "input_node");
     547              : 
     548            0 :     status = _ml_extension_conf_parse_pipeline_node (mls, node,
     549              :         ML_EXTENSION_NODE_TYPE_INPUT);
     550            0 :     if (status != ML_ERROR_NONE) {
     551            0 :       _ml_error_report_return (status,
     552              :           "Failed to parse configuration file, cannot get the input node.");
     553              :     }
     554              :   } else {
     555            0 :     _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     556              :         "Failed to parse configuration file, cannot find the input node.");
     557              :   }
     558              : 
     559            0 :   if (json_object_has_member (pipe, "output_node")) {
     560            0 :     JsonNode *node = json_object_get_member (pipe, "output_node");
     561              : 
     562            0 :     status = _ml_extension_conf_parse_pipeline_node (mls, node,
     563              :         ML_EXTENSION_NODE_TYPE_OUTPUT);
     564            0 :     if (status != ML_ERROR_NONE) {
     565            0 :       _ml_error_report_return (status,
     566              :           "Failed to parse configuration file, cannot get the output node.");
     567              :     }
     568              :   } else {
     569            0 :     _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     570              :         "Failed to parse configuration file, cannot find the output node.");
     571              :   }
     572              : 
     573              :   /* Start pipeline when creating ml-service handle to check pipeline description. */
     574            0 :   status = ml_pipeline_start (ext->pipeline);
     575            0 :   if (status != ML_ERROR_NONE) {
     576            0 :     _ml_error_report_return (status,
     577              :         "Failed to parse configuration file, cannot start the pipeline.");
     578              :   }
     579              : 
     580            0 :   return ML_ERROR_NONE;
     581              : }
     582              : 
     583              : /**
     584              :  * @brief Internal function to parse configuration file.
     585              :  */
     586              : static int
     587            0 : _ml_extension_conf_parse_json (ml_service_s * mls, JsonObject * object)
     588              : {
     589            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     590              :   int status;
     591              : 
     592            0 :   if (json_object_has_member (object, "single")) {
     593            0 :     JsonObject *single = json_object_get_object_member (object, "single");
     594              : 
     595            0 :     status = _ml_extension_conf_parse_single (mls, single);
     596            0 :     if (status != ML_ERROR_NONE)
     597            0 :       return status;
     598              : 
     599            0 :     ext->type = ML_EXTENSION_TYPE_SINGLE;
     600            0 :   } else if (json_object_has_member (object, "pipeline")) {
     601            0 :     JsonObject *pipe = json_object_get_object_member (object, "pipeline");
     602              : 
     603            0 :     status = _ml_extension_conf_parse_pipeline (mls, pipe);
     604            0 :     if (status != ML_ERROR_NONE)
     605            0 :       return status;
     606              : 
     607            0 :     ext->type = ML_EXTENSION_TYPE_PIPELINE;
     608              :   } else {
     609            0 :     _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     610              :         "Failed to parse configuration file, cannot get the valid type from configuration.");
     611              :   }
     612              : 
     613            0 :   return ML_ERROR_NONE;
     614              : }
     615              : 
     616              : /**
     617              :  * @brief Internal function to create ml-service extension.
     618              :  */
     619              : int
     620            0 : ml_service_extension_create (ml_service_s * mls, JsonObject * object)
     621              : {
     622              :   ml_extension_s *ext;
     623            0 :   g_autofree gchar *thread_name = g_strdup_printf ("ml-ext-msg-%d", getpid ());
     624              :   int status;
     625              : 
     626            0 :   mls->priv = ext = g_try_new0 (ml_extension_s, 1);
     627            0 :   if (ext == NULL) {
     628            0 :     _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
     629              :         "Failed to allocate memory for ml-service extension. Out of memory?");
     630              :   }
     631              : 
     632            0 :   ext->type = ML_EXTENSION_TYPE_UNKNOWN;
     633            0 :   ext->running = FALSE;
     634            0 :   ext->timeout = DEFAULT_TIMEOUT;
     635            0 :   ext->max_input = DEFAULT_MAX_INPUT;
     636            0 :   ext->node_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
     637              :       _ml_extension_node_info_free);
     638              : 
     639            0 :   status = _ml_extension_conf_parse_json (mls, object);
     640            0 :   if (status != ML_ERROR_NONE) {
     641            0 :     _ml_error_report_return (status,
     642              :         "Failed to parse the ml-service extension configuration.");
     643              :   }
     644              : 
     645            0 :   g_mutex_lock (&mls->lock);
     646              : 
     647            0 :   ext->msg_queue = g_async_queue_new_full (_ml_extension_msg_free);
     648            0 :   ext->msg_thread = g_thread_new (thread_name, _ml_extension_msg_thread, mls);
     649              : 
     650              :   /* Wait until the message thread has been initialized. */
     651            0 :   g_cond_wait (&mls->cond, &mls->lock);
     652            0 :   g_mutex_unlock (&mls->lock);
     653              : 
     654            0 :   return ML_ERROR_NONE;
     655              : }
     656              : 
     657              : /**
     658              :  * @brief Internal function to release ml-service extension.
     659              :  */
     660              : int
     661            0 : ml_service_extension_destroy (ml_service_s * mls)
     662              : {
     663            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     664              : 
     665              :   /* Supposed internal function call to release handle. */
     666            0 :   if (!ext)
     667            0 :     return ML_ERROR_NONE;
     668              : 
     669              :   /**
     670              :    * Close message thread.
     671              :    * If model inference is running, it may wait for the result in message thread.
     672              :    * This takes time, so do not call join with extension lock.
     673              :    */
     674            0 :   ext->running = FALSE;
     675            0 :   if (ext->msg_thread) {
     676            0 :     g_thread_join (ext->msg_thread);
     677            0 :     ext->msg_thread = NULL;
     678              :   }
     679              : 
     680            0 :   if (ext->msg_queue) {
     681            0 :     g_async_queue_unref (ext->msg_queue);
     682            0 :     ext->msg_queue = NULL;
     683              :   }
     684              : 
     685            0 :   if (ext->single) {
     686            0 :     ml_single_close (ext->single);
     687            0 :     ext->single = NULL;
     688              :   }
     689              : 
     690            0 :   if (ext->pipeline) {
     691            0 :     ml_pipeline_stop (ext->pipeline);
     692            0 :     ml_pipeline_destroy (ext->pipeline);
     693            0 :     ext->pipeline = NULL;
     694              :   }
     695              : 
     696            0 :   if (ext->node_table) {
     697            0 :     g_hash_table_destroy (ext->node_table);
     698            0 :     ext->node_table = NULL;
     699              :   }
     700              : 
     701            0 :   g_free (ext);
     702            0 :   mls->priv = NULL;
     703              : 
     704            0 :   return ML_ERROR_NONE;
     705              : }
     706              : 
     707              : /**
     708              :  * @brief Internal function to start ml-service extension.
     709              :  */
     710              : int
     711            0 : ml_service_extension_start (ml_service_s * mls)
     712              : {
     713            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     714            0 :   int status = ML_ERROR_NONE;
     715              : 
     716            0 :   switch (ext->type) {
     717            0 :     case ML_EXTENSION_TYPE_PIPELINE:
     718            0 :       status = ml_pipeline_start (ext->pipeline);
     719            0 :       break;
     720            0 :     case ML_EXTENSION_TYPE_SINGLE:
     721              :       /* Do nothing. */
     722            0 :       break;
     723            0 :     default:
     724            0 :       status = ML_ERROR_NOT_SUPPORTED;
     725            0 :       break;
     726              :   }
     727              : 
     728            0 :   return status;
     729              : }
     730              : 
     731              : /**
     732              :  * @brief Internal function to stop ml-service extension.
     733              :  */
     734              : int
     735            0 : ml_service_extension_stop (ml_service_s * mls)
     736              : {
     737            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     738            0 :   int status = ML_ERROR_NONE;
     739              : 
     740            0 :   switch (ext->type) {
     741            0 :     case ML_EXTENSION_TYPE_PIPELINE:
     742            0 :       status = ml_pipeline_stop (ext->pipeline);
     743            0 :       break;
     744            0 :     case ML_EXTENSION_TYPE_SINGLE:
     745              :       /* Do nothing. */
     746            0 :       break;
     747            0 :     default:
     748            0 :       status = ML_ERROR_NOT_SUPPORTED;
     749            0 :       break;
     750              :   }
     751              : 
     752            0 :   return status;
     753              : }
     754              : 
     755              : /**
     756              :  * @brief Internal function to get the information of required input data.
     757              :  */
     758              : int
     759            0 : ml_service_extension_get_input_information (ml_service_s * mls,
     760              :     const char *name, ml_tensors_info_h * info)
     761              : {
     762            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     763              :   int status;
     764              : 
     765            0 :   switch (ext->type) {
     766            0 :     case ML_EXTENSION_TYPE_SINGLE:
     767            0 :       status = ml_single_get_input_info (ext->single, info);
     768            0 :       break;
     769            0 :     case ML_EXTENSION_TYPE_PIPELINE:
     770              :     {
     771              :       ml_extension_node_info_s *node_info;
     772              : 
     773            0 :       node_info = _ml_extension_node_info_get (ext, name);
     774              : 
     775            0 :       if (node_info && node_info->type == ML_EXTENSION_NODE_TYPE_INPUT) {
     776            0 :         status = _ml_tensors_info_create_from (node_info->info, info);
     777              :       } else {
     778            0 :         status = ML_ERROR_INVALID_PARAMETER;
     779              :       }
     780            0 :       break;
     781              :     }
     782            0 :     default:
     783            0 :       status = ML_ERROR_NOT_SUPPORTED;
     784            0 :       break;
     785              :   }
     786              : 
     787            0 :   return status;
     788              : }
     789              : 
     790              : /**
     791              :  * @brief Internal function to get the information of output data.
     792              :  */
     793              : int
     794            0 : ml_service_extension_get_output_information (ml_service_s * mls,
     795              :     const char *name, ml_tensors_info_h * info)
     796              : {
     797            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     798              :   int status;
     799              : 
     800            0 :   switch (ext->type) {
     801            0 :     case ML_EXTENSION_TYPE_SINGLE:
     802            0 :       status = ml_single_get_output_info (ext->single, info);
     803            0 :       break;
     804            0 :     case ML_EXTENSION_TYPE_PIPELINE:
     805              :     {
     806              :       ml_extension_node_info_s *node_info;
     807              : 
     808            0 :       node_info = _ml_extension_node_info_get (ext, name);
     809              : 
     810            0 :       if (node_info && node_info->type == ML_EXTENSION_NODE_TYPE_OUTPUT) {
     811            0 :         status = _ml_tensors_info_create_from (node_info->info, info);
     812              :       } else {
     813            0 :         status = ML_ERROR_INVALID_PARAMETER;
     814              :       }
     815            0 :       break;
     816              :     }
     817            0 :     default:
     818            0 :       status = ML_ERROR_NOT_SUPPORTED;
     819            0 :       break;
     820              :   }
     821              : 
     822            0 :   if (status != ML_ERROR_NONE) {
     823            0 :     if (*info) {
     824            0 :       ml_tensors_info_destroy (*info);
     825            0 :       *info = NULL;
     826              :     }
     827              :   }
     828              : 
     829            0 :   return status;
     830              : }
     831              : 
     832              : /**
     833              :  * @brief Internal function to set the information for ml-service extension.
     834              :  */
     835              : int
     836            0 : ml_service_extension_set_information (ml_service_s * mls, const char *name,
     837              :     const char *value)
     838              : {
     839            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     840              : 
     841              :   /* Check limitation of message queue and other options. */
     842            0 :   if (g_ascii_strcasecmp (name, "input_queue_size") == 0 ||
     843            0 :       g_ascii_strcasecmp (name, "max_input") == 0) {
     844            0 :     ext->max_input = (guint) g_ascii_strtoull (value, NULL, 10);
     845            0 :   } else if (g_ascii_strcasecmp (name, "timeout") == 0) {
     846            0 :     ext->timeout = (guint) g_ascii_strtoull (value, NULL, 10);
     847              :   }
     848              : 
     849            0 :   return ML_ERROR_NONE;
     850              : }
     851              : 
     852              : /**
     853              :  * @brief Internal function to add an input data to process the model in ml-service extension handle.
     854              :  */
     855              : int
     856            0 : ml_service_extension_request (ml_service_s * mls, const char *name,
     857              :     const ml_tensors_data_h data)
     858              : {
     859            0 :   ml_extension_s *ext = (ml_extension_s *) mls->priv;
     860              :   ml_extension_msg_s *msg;
     861              :   int status, len;
     862              : 
     863            0 :   if (ext->type == ML_EXTENSION_TYPE_PIPELINE) {
     864              :     ml_extension_node_info_s *node_info;
     865              : 
     866            0 :     if (!STR_IS_VALID (name)) {
     867            0 :       _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     868              :           "The parameter, name '%s', is invalid.", name);
     869              :     }
     870              : 
     871            0 :     node_info = _ml_extension_node_info_get (ext, name);
     872              : 
     873            0 :     if (!node_info || node_info->type != ML_EXTENSION_NODE_TYPE_INPUT) {
     874            0 :       _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
     875              :           "The parameter, name '%s', is invalid, cannot find the input node from pipeline.",
     876              :           name);
     877              :     }
     878              :   }
     879              : 
     880            0 :   len = g_async_queue_length (ext->msg_queue);
     881              : 
     882            0 :   if (ext->max_input > 0 && len > 0 && ext->max_input <= len) {
     883            0 :     _ml_error_report_return (ML_ERROR_STREAMS_PIPE,
     884              :         "Failed to push input data into the queue, the max number of input is %u.",
     885              :         ext->max_input);
     886              :   }
     887              : 
     888            0 :   msg = g_try_new0 (ml_extension_msg_s, 1);
     889            0 :   if (!msg) {
     890            0 :     _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
     891              :         "Failed to allocate the ml-service extension message. Out of memory?");
     892              :   }
     893              : 
     894            0 :   msg->name = g_strdup (name);
     895            0 :   status = ml_tensors_data_clone (data, &msg->input);
     896              : 
     897            0 :   if (status != ML_ERROR_NONE) {
     898            0 :     _ml_extension_msg_free (msg);
     899            0 :     _ml_error_report_return (status, "Failed to clone input data.");
     900              :   }
     901              : 
     902            0 :   g_async_queue_push (ext->msg_queue, msg);
     903              : 
     904            0 :   return ML_ERROR_NONE;
     905              : }
        

Generated by: LCOV version 2.0-1