Move service engine status check logic into tts_core 91/246891/2
authorSuyeon Hwang <stom.hwang@samsung.com>
Wed, 4 Nov 2020 06:37:29 +0000 (15:37 +0900)
committerSuyeon Hwang <stom.hwang@samsung.com>
Fri, 13 Nov 2020 09:53:30 +0000 (18:53 +0900)
tts_core handles all prepare sequence and service engine status is only needed on prepare sequence.

This patch moves all the codes related with service engine status into tts_core.c/h.

Change-Id: Ic6ad2c947b0c6bd40add5842b6c90e0c2577dc56
Signed-off-by: Suyeon Hwang <stom.hwang@samsung.com>
client/tts.c
client/tts_core.c
client/tts_core.h

index e2ce767..f556cec 100644 (file)
@@ -45,13 +45,6 @@ static int g_voice_type = -1;
 
 static int g_speed = -1;
 
-/* for checking engine update */
-static pkgmgr_client* g_pkgmgr = NULL;
-static char* g_pkgmgr_status = NULL;
-static int g_engine_update_status = 0;
-static pthread_mutex_t g_pkgmgr_mutex = PTHREAD_MUTEX_INITIALIZER;
-static Ecore_Thread* g_pkgmgr_thread = NULL;
-static volatile bool g_is_finished_pkgmgr_thread = false;
 
 static int __tts_get_feature_enabled()
 {
@@ -77,6 +70,7 @@ static int __tts_get_feature_enabled()
        return 0;
 }
 
+// TODO: move into tts_core to make common function
 static const char* __tts_get_error_code(tts_error_e err)
 {
        switch (err) {
@@ -101,6 +95,7 @@ static const char* __tts_get_error_code(tts_error_e err)
        return NULL;
 }
 
+// TODO: move into tts_core to make common function
 static int __tts_convert_config_error_code(tts_config_error_e code)
 {
        if (code == TTS_CONFIG_ERROR_NONE)                      return TTS_ERROR_NONE;
@@ -261,158 +256,6 @@ void __tts_unset_all_callbacks(tts_h tts)
        SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
 }
 
-static int __pkgmgr_status_cb(uid_t target_uid, int req_id, const char *type, const char *pkgname, const char *key, const char *val, const void *pmsg, void *data)
-{
-       // type (the type of the pkgname)
-       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr status cb is invoked. pkgname(%s), type(%s), key(%s), val(%s)", pkgname, type, key, val);
-
-       const char* engine_name = tts_core_get_engine_name();
-       if (NULL == engine_name) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] engine name is NULL");
-               return 0;
-       }
-
-       if (0 != strncmp(engine_name, pkgname, strlen(engine_name))) {
-               SLOG(LOG_DEBUG, TAG_TTSC, "[WARN] this is not tts engine");
-               return 0;
-       } else {
-               if (key) {
-                       if (0 == strncmp(key, "start", strlen(key))) {
-                               if (NULL != g_pkgmgr_status) {
-                                       free(g_pkgmgr_status);
-                                       g_pkgmgr_status = NULL;
-                               }
-
-                               if (val) {
-                                       g_pkgmgr_status = strdup(val);
-                                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr status. key(%s), status(%s)", key, g_pkgmgr_status);
-
-                                               if ((0 == strncmp(val, "update", strlen(val) || 0 == strncmp(val, "uninstall", strlen(val))))) {
-                                               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] start to install.");
-                                               g_engine_update_status = 1;
-                                       }
-                               }
-                       } else if (0 == strncmp(key, "end", strlen(key)) && val && 0 == strncmp(val, "ok", strlen(val))) {
-                               if (g_pkgmgr_status) {
-                                       if (0 == strncmp(g_pkgmgr_status, "install", strlen(g_pkgmgr_status)) || 0 == strncmp(g_pkgmgr_status, "update", strlen(g_pkgmgr_status))) {
-                                               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] finish to install");
-                                               g_engine_update_status = 0;
-
-                                               free(g_pkgmgr_status);
-                                               g_pkgmgr_status = NULL;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static void __create_pkgmgr_thread(void* data, Ecore_Thread* thread)
-{
-       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] create pkgmgr thread");
-
-       g_is_finished_pkgmgr_thread = false;
-
-       tts_h tts = (tts_h)data;
-       tts_client_s* client = tts_client_get(tts);
-       /* check handle */
-       if (NULL == client) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] client is null");
-               g_is_finished_pkgmgr_thread = true;
-               return ;
-       }
-
-       pthread_mutex_lock(&g_pkgmgr_mutex);
-
-       while (!g_pkgmgr) {
-               g_pkgmgr = pkgmgr_client_new(PC_LISTENING);
-               if (NULL == g_pkgmgr) {
-                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to create pkgmgr handle");
-               } else {
-                       int ret = pkgmgr_client_set_status_type(g_pkgmgr, PKGMGR_CLIENT_STATUS_INSTALL | PKGMGR_CLIENT_STATUS_UNINSTALL | PKGMGR_CLIENT_STATUS_UPGRADE);
-                       if (0 == ret) {
-                               if (pkgmgr_client_listen_status(g_pkgmgr, __pkgmgr_status_cb, NULL) < 0) {
-                                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to listen pkgmgr status. remove and recreate client");
-                                       pkgmgr_client_free(g_pkgmgr);
-                                       g_pkgmgr = NULL;
-                                       usleep(10000);
-                                       continue;
-                               } else {
-                                       SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Succeed to register pkgmgr cb");
-                               }
-                       } else {
-                               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set status type on pkgmgr, ret(%d)", ret);
-                               pkgmgr_client_free(g_pkgmgr);
-                               g_pkgmgr = NULL;
-                               usleep(10000);
-                               continue;
-                       }
-               }
-               usleep(10000);
-
-               /* Checking the thread is canceled or not */
-               if (ecore_thread_check(g_pkgmgr_thread)) {
-                       SLOG(LOG_WARN, TAG_TTSC, "[WARNING] g_pkgmgr_thread is canceled. Exit");
-                       break;
-               }
-
-               /* Checking handle which can be destroyed on other thread */
-               if (false == tts_client_is_valid(tts)) {
-                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] client is already destroyed");
-                       break;
-               }
-       }
-
-       pthread_mutex_unlock(&g_pkgmgr_mutex);
-       g_is_finished_pkgmgr_thread = true;
-       SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Finish pkgmgr_thread. func_end will be invoked in main thread.");
-
-       return ;
-}
-
-static void __finish_pkgmgr_thread(void* data, Ecore_Thread* thread)
-{
-       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] Finish pkgmgr thread");
-       g_pkgmgr_thread = NULL;
-}
-
-static void __cancel_pkgmgr_thread(void* data, Ecore_Thread* thread)
-{
-       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] Cancel pkgmgr thread");
-       g_pkgmgr_thread = NULL;
-}
-
-static void __pkgmgr_thread(void* data)
-{
-       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] call pkgmgr_thread");
-
-       tts_h tts = (tts_h)data;
-       if (NULL == tts) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Input handle is null");
-               return ;
-       }
-
-       if (NULL == g_pkgmgr_thread) {
-               SLOG(LOG_INFO, TAG_TTSC, "[INFO] ecore thread run: create_pkgmgr_thread");
-               g_pkgmgr_thread = ecore_thread_run(__create_pkgmgr_thread, __finish_pkgmgr_thread, __cancel_pkgmgr_thread, tts);
-       }
-       return ;
-}
-
-static void __tts_config_engine_changed_cb(keynode_t* key, void* data)
-{
-       SLOG(LOG_INFO, TAG_TTSC, "[INFO] tts engine vconfkey is changed");
-
-       if (0 != tts_core_update_engine_name()) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set engine name into core module");
-               return;
-       }
-
-       return;
-}
-
 int tts_create(tts_h* tts)
 {
        if (0 != __tts_get_feature_enabled()) {
@@ -428,11 +271,9 @@ int tts_create(tts_h* tts)
                return TTS_ERROR_INVALID_PARAMETER;
        }
 
+       bool is_first_client = false;
        if (0 == tts_client_get_size()) {
-               if (0 != tts_dbus_open_connection()) {
-                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to open dbus connection");
-                       return TTS_ERROR_OPERATION_FAILED;
-               }
+               is_first_client = true;
        }
 
        if (0 != tts_client_new(tts)) {
@@ -462,21 +303,22 @@ int tts_create(tts_h* tts)
                return __tts_convert_config_error_code(ret);
        }
 
-       //TODO: move to tts_core
-       ecore_main_loop_thread_safe_call_async(__pkgmgr_thread, *tts);
-
-       SLOG(LOG_INFO, TAG_TTSC, "[INFO] call ecore thread for creating pkgmgr thread");
+       if (is_first_client) {
+               // These function would be called only when first client is created.
+               if (0 != tts_dbus_open_connection()) {
+                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to open dbus connection");
+                       tts_client_destroy(*tts);
+                       return TTS_ERROR_OPERATION_FAILED;
+               }
 
-       if (0 != tts_core_update_engine_name()) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set engine name into core module");
-               return TTS_ERROR_OPERATION_FAILED;
+               if (0 != tts_core_initialize()) {
+                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to initialize core");
+                       tts_client_destroy(*tts);
+                       return TTS_ERROR_OPERATION_FAILED;
+               }
        }
 
-       /* Register vconfkey callback to detect engine change */
-       vconf_notify_key_changed(TTS_ENGINE_DB_DEFAULT, __tts_config_engine_changed_cb, client->tts);
-
        SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
-
        return TTS_ERROR_NONE;
 }
 
@@ -509,9 +351,6 @@ int tts_destroy(tts_h tts)
                return TTS_ERROR_OPERATION_FAILED;
        }
 
-       /* Unregister vconfkey callback */
-       vconf_ignore_key_changed(TTS_ENGINE_DB_DEFAULT, __tts_config_engine_changed_cb);
-
        tts_config_mgr_finalize(client->uid);
 
        if (client->hello_timer) {
@@ -570,10 +409,6 @@ int tts_destroy(tts_h tts)
                __tts_unset_all_callbacks(tts);
 
                /* Cancel and Check threads */
-               if (NULL != g_pkgmgr_thread && false == ecore_thread_check(g_pkgmgr_thread) && false == g_is_finished_pkgmgr_thread) {
-                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr thread is not finished. Cancel pkgmgr thread");
-                       ecore_thread_cancel(g_pkgmgr_thread);
-               }
                if (NULL != client->thread && false == ecore_thread_check(client->thread)) {
                        SLOG(LOG_INFO, TAG_TTSC, "[INFO] Cancel client thread");
                        ecore_thread_cancel(client->thread);
@@ -618,17 +453,10 @@ int tts_destroy(tts_h tts)
                if (0 != tts_dbus_close_connection()) {
                        SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to close connection");
                }
-               pthread_mutex_lock(&g_pkgmgr_mutex);
-               if (g_pkgmgr) {
-                       pkgmgr_client_remove_listen_status(g_pkgmgr);
-                       pkgmgr_client_free(g_pkgmgr);
-                       g_pkgmgr = NULL;
-               }
-               if (NULL != g_pkgmgr_status) {
-                       free(g_pkgmgr_status);
-                       g_pkgmgr_status = NULL;
+
+               if (0 != tts_core_deinitialize()) {
+                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to deinitialize core");
                }
-               pthread_mutex_unlock(&g_pkgmgr_mutex);
        } else {
                SLOG(LOG_ERROR, TAG_TTSC, "[INFO] num_of_client(%d)", num_of_client);
        }
@@ -638,9 +466,6 @@ int tts_destroy(tts_h tts)
                g_language = NULL;
        }
 
-
-       tts = NULL;
-
        SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
 
        return TTS_ERROR_NONE;
index f22a479..2de6ca8 100644 (file)
 #include "tts_core.h"
 
 /* Static variables */
+static volatile bool g_is_thread_canceled = false;
+
 static char* g_engine_name = NULL;
 static int g_engine_update_status = 0;
 
+static char* g_pkgmgr_status = NULL;
 static pkgmgr_client* g_pkgmgr = NULL;
+static Ecore_Thread* g_pkgmgr_thread = NULL;
+static pthread_mutex_t g_pkgmgr_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 
 /* Static functions */
@@ -75,6 +80,23 @@ static char* __get_engine_appid(int mode) {
        return appid;
 }
 
+static int __update_engine_name()
+{
+       if (g_engine_name) {
+               free(g_engine_name);
+               g_engine_name = NULL;
+       }
+
+       g_engine_name = vconf_get_str(TTS_ENGINE_DB_DEFAULT);
+       if (NULL == g_engine_name) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to get engine name");
+               return TTS_ERROR_OPERATION_FAILED;
+       }
+
+       SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Engine name(%s)", g_engine_name);
+       return TTS_ERROR_NONE;
+}
+
 static inline void __client_error_cb(tts_client_s* client, int utt_id, tts_error_e reason)
 {
        if (NULL != client->error_cb) {
@@ -178,6 +200,127 @@ static bool __is_engine_launched(int mode)
        return is_running;
 }
 
+static int __pkgmgr_status_cb(uid_t target_uid, int req_id, const char *type, const char *pkgname, const char *key, const char *val, const void *pmsg, void *data)
+{
+       // type (the type of the pkgname)
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr status cb is invoked. pkgname(%s), type(%s), key(%s), val(%s)", pkgname, type, key, val);
+
+       if (NULL == g_engine_name) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] engine name is NULL");
+               return 0;
+       }
+
+       if (0 != strncmp(g_engine_name, pkgname, strlen(g_engine_name))) {
+               SLOG(LOG_DEBUG, TAG_TTSC, "[DEBUG] this is not tts engine");
+               return 0;
+       }
+
+       if (NULL == key) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] key is NULL");
+               return 0;
+       }
+
+       if (0 == strncmp(key, "start", strlen(key))) {
+               if (NULL != g_pkgmgr_status) {
+                       free(g_pkgmgr_status);
+                       g_pkgmgr_status = NULL;
+               }
+
+               if (val) {
+                       g_pkgmgr_status = strdup(val);
+                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr status. key(%s), status(%s)", key, g_pkgmgr_status);
+
+                       if ((0 == strncmp(val, "update", strlen(val) || 0 == strncmp(val, "uninstall", strlen(val))))) {
+                               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] start to install.");
+                               g_engine_update_status = 1;
+                       }
+               }
+       } else if (0 == strncmp(key, "end", strlen(key)) && val && 0 == strncmp(val, "ok", strlen(val))) {
+               if (g_pkgmgr_status) {
+                       if (0 == strncmp(g_pkgmgr_status, "install", strlen(g_pkgmgr_status)) || 0 == strncmp(g_pkgmgr_status, "update", strlen(g_pkgmgr_status))) {
+                               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] finish to install");
+                               g_engine_update_status = 0;
+
+                               free(g_pkgmgr_status);
+                               g_pkgmgr_status = NULL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void __create_pkgmgr_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] create pkgmgr thread");
+
+       pthread_mutex_lock(&g_pkgmgr_mutex);
+       while (NULL == g_pkgmgr) {
+               /* Checking the thread is canceled or not */
+               if (g_is_thread_canceled) {
+                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] g_pkgmgr_thread is canceled. Exit");
+                       break;
+               }
+
+               /* Checking handle which can be destroyed on other thread */
+               if (0 == tts_client_get_size()) {
+                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] All client is already destroyed");
+                       break;
+               }
+
+               g_pkgmgr = pkgmgr_client_new(PC_LISTENING);
+               if (NULL == g_pkgmgr) {
+                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to create pkgmgr handle");
+               } else {
+                       int ret = pkgmgr_client_set_status_type(g_pkgmgr, PKGMGR_CLIENT_STATUS_INSTALL | PKGMGR_CLIENT_STATUS_UNINSTALL | PKGMGR_CLIENT_STATUS_UPGRADE);
+                       if (0 == ret) {
+                               if (pkgmgr_client_listen_status(g_pkgmgr, __pkgmgr_status_cb, NULL) < 0) {
+                                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to listen pkgmgr status. remove and recreate client");
+                                       pkgmgr_client_free(g_pkgmgr);
+                                       g_pkgmgr = NULL;
+                               } else {
+                                       SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Succeed to register pkgmgr cb");
+                                       break;
+                               }
+                       } else {
+                               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set status type on pkgmgr, ret(%d)", ret);
+                               pkgmgr_client_free(g_pkgmgr);
+                               g_pkgmgr = NULL;
+                       }
+               }
+
+               usleep(10000);
+       }
+       pthread_mutex_unlock(&g_pkgmgr_mutex);
+
+       SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] create pkgmgr end");
+}
+
+static void __finish_pkgmgr_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] Finish pkgmgr thread");
+       g_is_thread_canceled = false;
+       g_pkgmgr_thread = NULL;
+}
+
+static void __cancel_pkgmgr_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] Cancel pkgmgr thread");
+       g_is_thread_canceled = false;
+       g_pkgmgr_thread = NULL;
+}
+
+static void __pkgmgr_thread(void* data)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] call pkgmgr_thread");
+       if (NULL == g_pkgmgr_thread) {
+               SLOG(LOG_INFO, TAG_TTSC, "[INFO] ecore thread run: create_pkgmgr_thread");
+               g_pkgmgr_thread = ecore_thread_run(__create_pkgmgr_thread, __finish_pkgmgr_thread, __cancel_pkgmgr_thread, NULL);
+       }
+
+       return ;
+}
+
 static void __start_reprepare_thread(void* data, Ecore_Thread* thread)
 {
        SLOG(LOG_INFO, TAG_TTSC, "[DEBUG] start reprepare thread. engine update status(%d)", g_engine_update_status);
@@ -435,8 +578,58 @@ static Eina_Bool __prepare_sync_cb(tts_client_s* client)
        return EINA_FALSE;
 }
 
+static void __engine_changed_cb(keynode_t* key, void* data)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] tts engine vconfkey is changed");
+       if (0 != __update_engine_name()) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set engine name into core module");
+       }
+
+       return;
+}
+
 
 /* Public functions */
+int tts_core_initialize()
+{
+       ecore_main_loop_thread_safe_call_async(__pkgmgr_thread, NULL);
+
+       if (0 != __update_engine_name()) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set engine name into core module");
+       }
+
+       /* Register vconfkey callback to detect engine change */
+       vconf_notify_key_changed(TTS_ENGINE_DB_DEFAULT, __engine_changed_cb, NULL);
+
+       return TTS_ERROR_NONE;
+}
+
+int tts_core_deinitialize()
+{
+       if (NULL != g_pkgmgr_thread) {
+               SLOG(LOG_INFO, TAG_TTSC, "[INFO] Cancel pkgmgr thread");
+               g_is_thread_canceled = true;
+       }
+
+       pthread_mutex_lock(&g_pkgmgr_mutex);
+       if (NULL != g_pkgmgr) {
+               pkgmgr_client_remove_listen_status(g_pkgmgr);
+               pkgmgr_client_free(g_pkgmgr);
+               g_pkgmgr = NULL;
+       }
+
+       if (NULL != g_pkgmgr_status) {
+               free(g_pkgmgr_status);
+               g_pkgmgr_status = NULL;
+       }
+       pthread_mutex_unlock(&g_pkgmgr_mutex);
+
+       /* Unregister vconfkey callback */
+       vconf_ignore_key_changed(TTS_ENGINE_DB_DEFAULT, __engine_changed_cb);
+
+       return TTS_ERROR_NONE;
+}
+
 int tts_core_notify_state_changed(tts_client_s* client, tts_state_e before_state, tts_state_e current_state)
 {
        /* check handle */
@@ -682,23 +875,6 @@ int tts_core_receive_hello(int uid, int ret, int credential_needed)
        return TTS_ERROR_NONE;
 }
 
-int tts_core_update_engine_name()
-{
-       if (g_engine_name) {
-               free(g_engine_name);
-               g_engine_name = NULL;
-       }
-
-       g_engine_name = vconf_get_str(TTS_ENGINE_DB_DEFAULT);
-       if (NULL == g_engine_name) {
-               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to get engine name");
-               return TTS_ERROR_OPERATION_FAILED;
-       }
-
-       SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Engine name(%s)", g_engine_name);
-       return TTS_ERROR_NONE;
-}
-
 const char* tts_core_get_engine_name()
 {
        return g_engine_name;
index 3d294dd..e3f75f7 100644 (file)
@@ -33,7 +33,8 @@ int tts_core_notify_engine_changed(tts_client_s* client, const char* engine_id,
 int tts_core_notify_supported_voice(tts_client_s* client, const char* language, int voice_type);
 
 // called by tts.c
-int tts_core_update_engine_name();
+int tts_core_initialize();
+int tts_core_deinitialize();
 const char* tts_core_get_engine_name();
 
 int tts_core_prepare(tts_client_s* client);