Merge "Fix race condition when player thread is terminated" into tizen
authorWonnam Jang <wn.jang@samsung.com>
Wed, 3 Jan 2024 05:35:30 +0000 (05:35 +0000)
committerGerrit Code Review <gerrit@review>
Wed, 3 Jan 2024 05:35:30 +0000 (05:35 +0000)
client/tts.c
include/tts.h
include/tts_internal.h
packaging/tts.spec
server/ttsd_data.cpp
server/ttsd_engine_agent.c
tests/src/tts_unittests.cpp

index c47ef85..5bb7037 100644 (file)
@@ -299,17 +299,6 @@ static int destroy_tts_handle(tts_h tts)
                /* Unset registered callbacks */
                tts_client_unset_all_cb(client);
 
-               int thread_count = ecore_thread_active_get();
-               SLOG(LOG_INFO, TAG_TTSC, "[INFO] Active thread count: %d", thread_count);
-               for (int cnt = 0; 0 < thread_count; cnt++) {
-                       usleep(50000);
-                       if (30 == cnt) {
-                               SLOG(LOG_WARN, TAG_TTSC, "[WARNNING] Thread is blocked, %d", thread_count);
-                               break;
-                       }
-                       thread_count = ecore_thread_active_get();
-               }
-
                if (0 != tts_ipc_close_connection(uid)) {
                        SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to close connection");
                        return TTS_ERROR_OPERATION_FAILED;
index 95430ae..bd4e452 100644 (file)
@@ -40,7 +40,7 @@ extern "C" {
 
 /**
  * @brief Enumeration for error code.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 typedef enum {
        TTS_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
@@ -56,15 +56,15 @@ typedef enum {
        TTS_ERROR_ENGINE_NOT_FOUND = TIZEN_ERROR_TTS | 0x03, /**< No available engine */
        TTS_ERROR_OPERATION_FAILED = TIZEN_ERROR_TTS | 0x04, /**< Operation failed */
        TTS_ERROR_AUDIO_POLICY_BLOCKED = TIZEN_ERROR_TTS | 0x05, /**< Audio policy blocked */
-       TTS_ERROR_NOT_SUPPORTED_FEATURE = TIZEN_ERROR_TTS | 0x06, /**< Not supported feature of current engine @if MOBILE (Since 3.0) @elseif WEARABLE (Since 2.3.2) @endif */
-       TTS_ERROR_SERVICE_RESET = TIZEN_ERROR_TTS | 0x07, /**< Service reset @if MOBILE (Since 3.0) @elseif WEARABLE (Since 2.3.2) @endif */
+       TTS_ERROR_NOT_SUPPORTED_FEATURE = TIZEN_ERROR_TTS | 0x06, /**< Not supported feature of current engine (Since 3.0) */
+       TTS_ERROR_SERVICE_RESET = TIZEN_ERROR_TTS | 0x07, /**< Service reset (Since 3.0) */
        TTS_ERROR_SCREEN_READER_OFF = TIZEN_ERROR_TTS | 0x08 /**< Screen reader is off (Since 6.5) */
 } tts_error_e;
 
 
 /**
  * @brief Enumeration for TTS mode.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 typedef enum {
        TTS_MODE_DEFAULT = 0, /**< Default mode for normal application */
@@ -75,7 +75,7 @@ typedef enum {
 
 /**
  * @brief Enumeration for state.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 typedef enum {
        TTS_STATE_CREATED = 0, /**< 'CREATED' state */
@@ -132,42 +132,42 @@ typedef enum {
 
 /**
  * @brief Definition for automatic speaking speed.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 #define TTS_SPEED_AUTO         0
 
 
 /**
  * @brief Definition for automatic voice type.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 #define TTS_VOICE_TYPE_AUTO    0
 
 
 /**
  * @brief Definition for male voice type.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 #define TTS_VOICE_TYPE_MALE    1
 
 
 /**
  * @brief Definition for female voice type.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 #define TTS_VOICE_TYPE_FEMALE  2
 
 
 /**
  * @brief Definition for child voice type.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 #define TTS_VOICE_TYPE_CHILD   3
 
 
 /**
  * @brief The TTS handle.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
 */
 typedef struct tts_s *tts_h;
 
@@ -176,7 +176,7 @@ typedef struct tts_s *tts_h;
  * @brief Called when the state of TTS is changed.
  * @details If the daemon must stop player because of changing engine and
  *          the daemon must pause player because of other requests, this callback function is called.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] previous The previous state
  * @param[in] current The current state
@@ -190,7 +190,7 @@ typedef void (*tts_state_changed_cb)(tts_h tts, tts_state_e previous, tts_state_
 
 /**
  * @brief Called when utterance has started.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] utt_id The utterance ID passed from the add text function
  * @param[in] user_data The user data passed from the callback registration function
@@ -204,7 +204,7 @@ typedef void (*tts_utterance_started_cb)(tts_h tts, int utt_id, void* user_data)
 
 /**
  * @brief Called when utterance is finished.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] utt_id The utterance ID passed from the add text function
  * @param[in] user_data The user data passed from the callback registration function
@@ -218,7 +218,7 @@ typedef void (*tts_utterance_completed_cb)(tts_h tts, int utt_id, void *user_dat
 
 /**
  * @brief Called when an error occurs.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] utt_id The utterance ID passed from the add text function
  * @param[in] reason The error code
@@ -235,7 +235,7 @@ typedef void (*tts_error_cb)(tts_h tts, int utt_id, tts_error_e reason, void* us
 
 /**
  * @brief Called to retrieve the supported voice.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] language Language specified as an ISO 3166 alpha-2 two letter country-code followed by ISO 639-1 for the two-letter language code (for example, "ko_KR" for Korean, "en_US" for American English)
  * @param[in] voice_type A voice type (e.g. #TTS_VOICE_TYPE_MALE, #TTS_VOICE_TYPE_FEMALE)
@@ -250,7 +250,7 @@ typedef bool(*tts_supported_voice_cb)(tts_h tts, const char* language, int voice
 
 /**
  * @brief Called when the default voice is changed.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] previous_language The previous language
  * @param[in] previous_voice_type The previous voice type
@@ -265,7 +265,7 @@ typedef void (*tts_default_voice_changed_cb)(tts_h tts, const char* previous_lan
 
 /**
  * @brief Called when the engine is changed.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @param[in] tts The TTS handle
  * @param[in] engine_id Engine ID
  * @param[in] language The default language specified as an ISO 3166 alpha-2 two letter country-code followed by ISO 639-1 for the two-letter language code (for example, "ko_KR" for Korean, "en_US" for American English)
@@ -324,7 +324,7 @@ typedef void (*tts_synthesized_pcm_cb)(tts_h tts, int utt_id, tts_synthesized_pc
 
 /**
  * @brief Creates a handle for TTS.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @remarks If the function succeeds, @a tts handle must be released with tts_destroy().
  * @param[out] tts The TTS handle
  * @return @c 0 on success,
@@ -343,7 +343,7 @@ int tts_create(tts_h* tts);
 
 /**
  * @brief Destroys the handle and disconnects the daemon.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -358,7 +358,7 @@ int tts_destroy(tts_h tts);
 
 /**
  * @brief Sets the TTS mode.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] mode The mode
  * @return @c 0 on success,
@@ -376,7 +376,7 @@ int tts_set_mode(tts_h tts, tts_mode_e mode);
 
 /**
  * @brief Gets the TTS mode.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[out] mode The mode
  * @return @c 0 on success,
@@ -396,7 +396,7 @@ int tts_get_mode(tts_h tts, tts_mode_e* mode);
  * @details Using this API, the application can set a credential.
  *          The credential is a key to verify the authorization about using the engine.
  *          If the application sets the credential, it will be able to use functions of the engine entirely.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @remarks The necessity of the credential depends on the engine. In case of the engine which is basically embedded in Tizen, the credential is not necessary so far.
  *          However, if the user wants to apply the 3rd party's engine, the credential may be necessary. In that case, please follow the policy provided by the corresponding engine.
  * @param[in] tts The TTS handle
@@ -415,7 +415,7 @@ int tts_set_credential(tts_h tts, const char* credential);
 
 /**
  * @brief Connects the daemon asynchronously.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -434,7 +434,7 @@ int tts_prepare(tts_h tts);
 
 /**
  * @brief Disconnects the daemon.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -469,7 +469,7 @@ int tts_prepare_sync(tts_h tts);
 
 /**
  * @brief Retrieves all supported voices of the current engine using callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to invoke
  * @param[in] user_data The user data to be passed to the callback function
@@ -488,7 +488,7 @@ int tts_foreach_supported_voices(tts_h tts, tts_supported_voice_cb callback, voi
 
 /**
  * @brief Gets the default voice set by the user.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @remarks If the function succeeds, @a language must be released with free().
  * @param[in] tts The TTS handle
  * @param[out] language Language specified as an ISO 3166 alpha-2 two letter country-code followed by ISO 639-1 for the two-letter language code (for example, "ko_KR" for Korean, "en_US" for American English)
@@ -511,7 +511,7 @@ int tts_get_default_voice(tts_h tts, char** language, int* voice_type);
  *          Using this API, the application can set the private data and use the corresponding key of the engine.
  *          For example, if the engine provides 'girl's voice' as a voice type, the application can set the private data as the following.
  *          int ret = tts_set_private_data(#tts_h, "voice_type", "GIRL");
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @remarks If the engine is replaced with the other engine, the key may be ignored.
  * @param[in] tts The TTS handle
  * @param[in] key The field name of private data
@@ -534,7 +534,7 @@ int tts_set_private_data(tts_h tts, const char* key, const char* data);
  * @brief Gets the private data from tts engine.
  * @details The private data is the information provided by the engine.
  *          Using this API, the application can get the private data which corresponds to the key from the engine.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @remarks The @a data must be released using free() when it is no longer required.
  *          If the engine is replaced with the other engine, the key may be ignored.
  * @param[in] tts The TTS handle
@@ -556,7 +556,7 @@ int tts_get_private_data(tts_h tts, const char* key, char** data);
 
 /**
  * @brief Gets the maximum byte size for text.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[out] size The maximum byte size for text
  * @return @c 0 on success,
@@ -574,7 +574,7 @@ int tts_get_max_text_size(tts_h tts, unsigned int* size);
 
 /**
  * @brief Gets the current state of TTS.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[out] state The current state of TTS
  * @return @c 0 on success,
@@ -591,7 +591,7 @@ int tts_get_state(tts_h tts, tts_state_e* state);
 
 /**
  * @brief Gets the speed range.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[out] min The minimum speed value
  * @param[out] normal The normal speed value
@@ -611,7 +611,7 @@ int tts_get_speed_range(tts_h tts, int* min, int* normal, int* max);
 
 /**
  * @brief Gets the current error message.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @remarks This function should be called from a tts error callback. Calling in any other context will result in an Operation failed error.
  *          A successful call will allocate @a err_msg, which must be released by calling free() when it is no longer required.
  * @param[in] tts The TTS handle
@@ -669,7 +669,7 @@ int tts_check_screen_reader_on(tts_h tts, bool* is_on);
 
 /**
  * @brief Adds a text to the queue.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @remarks Locale(e.g. setlocale()) MUST be set for utf8 text validation check.
  * @param[in] tts The TTS handle
  * @param[in] text An input text based utf8
@@ -696,7 +696,7 @@ int tts_add_text(tts_h tts, const char* text, const char* language, int voice_ty
 
 /**
  * @brief Starts synthesizing voice from the text and plays the synthesized audio data.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -723,7 +723,7 @@ int tts_play(tts_h tts);
 
 /**
  * @brief Stops playing the utterance and clears the queue.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -744,7 +744,7 @@ int tts_stop(tts_h tts);
 
 /**
  * @brief Pauses the currently playing utterance.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -791,7 +791,7 @@ int tts_repeat(tts_h tts, char** text_repeat, int* utt_id);
 
 /**
  * @brief Registers a callback function to be called when the TTS state changes.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -810,7 +810,7 @@ int tts_set_state_changed_cb(tts_h tts, tts_state_changed_cb callback, void* use
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -826,7 +826,7 @@ int tts_unset_state_changed_cb(tts_h tts);
 
 /**
  * @brief Registers a callback function to detect utterance start.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -845,7 +845,7 @@ int tts_set_utterance_started_cb(tts_h tts, tts_utterance_started_cb callback, v
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -861,7 +861,7 @@ int tts_unset_utterance_started_cb(tts_h tts);
 
 /**
  * @brief Registers a callback function to detect utterance completion.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -880,7 +880,7 @@ int tts_set_utterance_completed_cb(tts_h tts, tts_utterance_completed_cb callbac
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -896,7 +896,7 @@ int tts_unset_utterance_completed_cb(tts_h tts);
 
 /**
  * @brief Registers a callback function to detect errors.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -915,7 +915,7 @@ int tts_set_error_cb(tts_h tts, tts_error_cb callback, void* user_data);
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -931,7 +931,7 @@ int tts_unset_error_cb(tts_h tts);
 
 /**
  * @brief Registers a callback function to detect default voice change.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -950,7 +950,7 @@ int tts_set_default_voice_changed_cb(tts_h tts, tts_default_voice_changed_cb cal
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @since_tizen 2.3
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
@@ -966,7 +966,7 @@ int tts_unset_default_voice_changed_cb(tts_h tts);
 
  /**
  * @brief Registers a callback function to detect the engine change.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @param[in] tts The TTS handle
  * @param[in] callback The callback function to register
  * @param[in] user_data The user data to be passed to the callback function
@@ -985,7 +985,7 @@ int tts_set_engine_changed_cb(tts_h tts, tts_engine_changed_cb callback, void* u
 
 /**
  * @brief Unregisters the callback function.
- * @since_tizen @if MOBILE 3.0 @elseif WEARABLE 2.3.2 @endif
+ * @since_tizen 3.0
  * @param[in] tts The TTS handle
  * @return @c 0 on success,
  *         otherwise a negative error value
index 11850c3..d92275e 100644 (file)
@@ -39,7 +39,7 @@ extern "C"
  * @details Using this API, the application can ask server tts with a credential.
  *  The credential is a key to verify the authorization about using the engine based on server, not embedded engine.
  *  If the application sets the credential, it will be able to use functions of the server engine entirely.
- * @since_tizen @if MOBILE 3.0
+ * @since_tizen 3.0
  *
  * @remarks The necessity of the credential depends on the engine. In case of the engine which is basically embedded in Tizen, the credential is not necessary so far.
  *  However, if the user wants to apply the 3rd party's engine, the credential may be necessary. In that case, please follow the policy provided by the corresponding engine.
index 0028871..0070c42 100644 (file)
@@ -1,6 +1,6 @@
 Name:       tts
 Summary:    Text To Speech client library and daemon
-Version:    1.80.9
+Version:    1.80.10
 Release:    1
 Group:      Graphics & UI Framework/Voice Framework
 License:    Apache-2.0
index 3ab1ce6..efca767 100644 (file)
@@ -245,6 +245,9 @@ static void clean_data(app_data_s& app_data)
 
        app_data.m_speak_data.clear();
        app_data.m_wav_data.clear();
+
+       free(app_data.credential);
+       app_data.credential = nullptr;
 }
 
 int ttsd_data_delete_client(unsigned int uid)
index 1339a6d..f6719cd 100644 (file)
@@ -294,6 +294,11 @@ int ttsd_engine_agent_release()
                g_engine_info->engine_path = NULL;
        }
 
+       if (NULL != g_engine_info->callbacks) {
+               free(g_engine_info->callbacks);
+               g_engine_info->callbacks = NULL;
+       }
+
        free(g_engine_info);
        g_engine_info = NULL;
 
index 6cca77c..c7b1b0b 100644 (file)
@@ -2175,6 +2175,11 @@ TEST_F(TTSTest, utc_tts_get_error_message_n1)
 
        EXPECT_EQ(tts_get_error_message(mHandle, nullptr), TTS_ERROR_INVALID_PARAMETER);
        EXPECT_EQ(tts_get_error_message(nullptr, &error_message), TTS_ERROR_INVALID_PARAMETER);
+
+       if (error_message) {
+               free(error_message);
+               error_message = nullptr;
+       }
 }
 
 /**