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

Generated by: LCOV version 2.4-0