From: Parichay Kapoor Date: Fri, 20 Sep 2019 07:22:27 +0000 (+0900) Subject: [single-new] Memory leak bug fix in error cases X-Git-Tag: accepted/tizen/unified/20190925.220436~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cb2552b1a09a8c1a502644b50255429ff416f620;p=platform%2Fupstream%2Fnnstreamer.git [single-new] Memory leak bug fix in error cases Added memory leak bug fix in error cases running Signed-off-by: Parichay Kapoor --- diff --git a/api/capi/src/nnstreamer-capi-single-new.c b/api/capi/src/nnstreamer-capi-single-new.c index ae33bfb..fbc48c2 100644 --- a/api/capi/src/nnstreamer-capi-single-new.c +++ b/api/capi/src/nnstreamer-capi-single-new.c @@ -72,7 +72,15 @@ G_LOCK_DEFINE_STATIC (magic); */ #define ML_SINGLE_HANDLE_UNLOCK(single_h) g_mutex_unlock (&single_h->mutex); -/* ML single api data structure for handle */ +/** States for invoke thread */ +typedef enum { + IDLE = 0, /**< ready to accept next input */ + RUNNING, /**< running an input, cannot accept more input */ + JOIN_REQUESTED, /**< should join the thread, will exit soon */ + ERROR /**< error on thread, will exit soon */ +} thread_state; + +/** ML single api data structure for handle */ typedef struct { GTensorFilterSingle *filter; /**< tensor filter element */ @@ -86,8 +94,8 @@ typedef struct ml_tensors_data_h input; /**< input received from user */ ml_tensors_data_h *output; /**< output to be sent back to user */ guint timeout; /**< timeout for invoking */ - gboolean data_ready; /**< data is ready to be processed */ - gboolean join; /**< thread should be joined */ + thread_state state; /**< current state of the thread */ + gboolean ignore_output; /**< ignore and free the output */ int status; /**< status of processing */ } ml_single; @@ -111,16 +119,16 @@ invoke_thread (void *arg) /** get the tensor_filter element */ klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE); if (!klass) { - single_h->join = TRUE; + single_h->state = ERROR; goto exit; } - while (single_h->join != TRUE) { + while (single_h->state <= RUNNING) { /** wait for data */ - while (single_h->data_ready != TRUE) { + while (single_h->state != RUNNING) { g_cond_wait (&single_h->cond, &single_h->mutex); - if (single_h->join == TRUE) + if (single_h->state >= JOIN_REQUESTED) goto exit; } @@ -152,7 +160,7 @@ invoke_thread (void *arg) g_mutex_lock (&single_h->mutex); /** Allocate output buffer */ - if (single_h->data_ready == TRUE) { + if (single_h->ignore_output == FALSE) { status = ml_tensors_data_create_no_alloc (&single_h->out_info, single_h->output); if (status != ML_ERROR_NONE) { @@ -166,17 +174,26 @@ invoke_thread (void *arg) for (i = 0; i < single_h->out_info.num_tensors; i++) { out_data->tensors[i].tensor = out_tensors[i].data; } + } else { + /** + * Caller of the invoke thread has returned back with timeout + * so, free the memory allocated by the invoke as their is no receiver + */ + for (i = 0; i < single_h->out_info.num_tensors; i++) + g_free (out_tensors[i].data); } /** loop over to wait for the next element */ wait_for_next: single_h->status = status; - single_h->data_ready = FALSE; + if (single_h->state == RUNNING) + single_h->state = IDLE; g_cond_broadcast (&single_h->cond); } exit: - single_h->data_ready = FALSE; + if (single_h->state != ERROR) + single_h->state = IDLE; g_mutex_unlock (&single_h->mutex); return NULL; } @@ -433,8 +450,8 @@ ml_single_open (ml_single_h * single, const char *model, g_mutex_init (&single_h->mutex); g_cond_init (&single_h->cond); - single_h->data_ready = FALSE; - single_h->join = FALSE; + single_h->state = IDLE; + single_h->ignore_output = FALSE; single_h->thread = g_thread_try_new (NULL, invoke_thread, (gpointer) single_h, &error); @@ -470,7 +487,7 @@ ml_single_close (ml_single_h single) ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 1); - single_h->join = TRUE; + single_h->state = JOIN_REQUESTED; g_cond_broadcast (&single_h->cond); ML_SINGLE_HANDLE_UNLOCK (single_h); @@ -520,7 +537,7 @@ ml_single_invoke (ml_single_h single, in_data = (ml_tensors_data_s *) input; *output = NULL; - if (!single_h->filter || single_h->join) { + if (!single_h->filter || single_h->state >= JOIN_REQUESTED) { ml_loge ("The given param is invalid, model is missing."); status = ML_ERROR_INVALID_PARAMETER; goto exit; @@ -545,14 +562,15 @@ ml_single_invoke (ml_single_h single, } } - if (single_h->data_ready == TRUE) { + if (single_h->state != IDLE) { status = ML_ERROR_TRY_AGAIN; goto exit; } single_h->input = input; single_h->output = output; - single_h->data_ready = TRUE; + single_h->state = RUNNING; + single_h->ignore_output = FALSE; end_time = g_get_monotonic_time () + single_h->timeout * G_TIME_SPAN_MILLISECOND; @@ -561,9 +579,16 @@ ml_single_invoke (ml_single_h single, if (g_cond_wait_until (&single_h->cond, &single_h->mutex, end_time)) { status = single_h->status; } else { + ml_logw ("Wait for invoke has timed out"); status = ML_ERROR_TIMED_OUT; /** This is set to notify invoke_thread to not process if timedout */ - single_h->data_ready = FALSE; + single_h->ignore_output = TRUE; + + /** Free if any output memory was allocated */ + if (*single_h->output != NULL) { + ml_tensors_data_destroy ((ml_tensors_data_h) *single_h->output); + *single_h->output = NULL; + } } exit: diff --git a/tests/tizen_capi/unittest_tizen_capi.cpp b/tests/tizen_capi/unittest_tizen_capi.cpp index 68e2690..5124f84 100644 --- a/tests/tizen_capi/unittest_tizen_capi.cpp +++ b/tests/tizen_capi/unittest_tizen_capi.cpp @@ -1940,7 +1940,8 @@ single_shot_loop_test (void *arg) status = ml_single_invoke (single, input, &output); if (ss_data->expect) { if (ss_data->timeout != 0 && ss_data->timeout < ss_data->min_time_to_run) { - EXPECT_EQ (status, ML_ERROR_TIMED_OUT); + EXPECT_TRUE (status == ML_ERROR_TIMED_OUT || + status == ML_ERROR_TRY_AGAIN); EXPECT_TRUE (output == NULL); } else { EXPECT_EQ (status, ML_ERROR_NONE); @@ -2016,11 +2017,14 @@ TEST (nnstreamer_capi_singleshot, invoke_timeout) /* check the old buffer is dropped */ status = ml_single_invoke (single, input, &output); - EXPECT_EQ (status, ML_ERROR_TIMED_OUT); + /* try_again implies that previous invoke hasn't finished yet */ + EXPECT_TRUE (status == ML_ERROR_TIMED_OUT || status == ML_ERROR_TRY_AGAIN); EXPECT_TRUE (output == NULL); /* set timeout 5 s */ status = ml_single_set_timeout (single, 5000); + /* clear out previous buffers */ + g_usleep (1000000); /** 1 sec */ status = ml_single_invoke (single, input, &output); EXPECT_EQ (status, ML_ERROR_NONE);