Commit 064d1195 authored by DanmeiChen's avatar DanmeiChen
Browse files

fix stop async core is suspended by background tasks and add unit test

parent 24af44ee
......@@ -154,7 +154,6 @@ static void set_media_network_reachable(LinphoneCore* lc,bool_t isReachable);
static void linphone_core_run_hooks(LinphoneCore *lc);
static void linphone_core_zrtp_cache_close(LinphoneCore *lc);
void linphone_core_zrtp_cache_db_init(LinphoneCore *lc, const char *fileName);
static void _linphone_core_stop_async_end(LinphoneCore *lc);
static LinphoneStatus _linphone_core_set_sip_transports(LinphoneCore *lc, const LinphoneSipTransports * tr_config, bool_t applyIt);
bool_t linphone_core_sound_resources_need_locking(LinphoneCore *lc, const LinphoneCallParams *params);
......@@ -2901,25 +2900,39 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
}
LinphoneStatus linphone_core_start (LinphoneCore *lc) {
try {
if (lc->state == LinphoneGlobalShutdown) {
//Force change of status to LinphoneGlobalOff, otherwise restarting it will fail
if (lc->state == LinphoneGlobalShutdown) {
_linphone_core_stop_async_end(lc);
}
if (lc->state == LinphoneGlobalOn) {
bctbx_warning("Core is already started, skipping...");
return -1;
} else if (lc->state == LinphoneGlobalShutdown) {
bctbx_error("Can't start a Core that is stopping, wait for Off state");
return -1;
} else if (lc->state == LinphoneGlobalOff) {
bctbx_warning("Core was stopped, before starting it again we need to init it");
linphone_core_init(lc, NULL, lc->config, lc->data, NULL, FALSE);
// Decrement refs to avoid leaking
linphone_config_unref(lc->config);
linphone_core_deactivate_log_serialization_if_needed();
bctbx_uninit_logger();
bctbx_warning("Core was shutDown, forcing to off");
_linphone_core_stop_async_end(lc);
}
try {
switch (lc->state) {
case LinphoneGlobalShutdown:
bctbx_error("Can't start a Core that is shutdown, wait for Off state");
return -1;
case LinphoneGlobalOff:
bctbx_warning("Core was Off, before starting it again we need to init it");
linphone_core_init(lc, NULL, lc->config, lc->data, NULL, FALSE);
// Decrement refs to avoid leaking
linphone_config_unref(lc->config);
linphone_core_deactivate_log_serialization_if_needed();
bctbx_uninit_logger();
break;
case LinphoneGlobalReady:
break;
case LinphoneGlobalStartup:
bctbx_warning("Core was startUp, skipping... (wait for On state)");
return -1;
case LinphoneGlobalConfiguring:
bctbx_warning("Core was Configuring, skipping... (wait for On state)");
return -1;
case LinphoneGlobalOn:
bctbx_warning("Core was On, skipping... ");
return -1;
}
if (!getPlatformHelpers(lc)->getSharedCoreHelpers()->canCoreStart()) {
......@@ -6847,6 +6860,9 @@ static void _linphone_core_stop_async_start(LinphoneCore *lc) {
linphone_core_stop_dtmf_stream(lc);
linphone_core_set_state(lc, LinphoneGlobalShutdown, "Shutdown");
#if TARGET_OS_IPHONE
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->onStopAsyncBackgroundTaskStarted();
#endif
}
/**
......@@ -6854,7 +6870,11 @@ static void _linphone_core_stop_async_start(LinphoneCore *lc) {
* After we made sure all asynchronous tasks are finished, this function is called to clean the objects
* and change the state to "Off"
*/
static void _linphone_core_stop_async_end(LinphoneCore *lc) {
void _linphone_core_stop_async_end(LinphoneCore *lc) {
#if TARGET_OS_IPHONE
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->onStopAsyncBackgroundTaskStopped();
#endif
// Call uninit here because there may be the need to access DB while unregistering
L_GET_PRIVATE_FROM_C_OBJECT(lc)->uninit();
lc->chat_rooms = bctbx_list_free_with_data(lc->chat_rooms, (bctbx_list_free_func)linphone_chat_room_unref);
......
......@@ -133,6 +133,7 @@ LINPHONE_PUBLIC void linphone_call_start_push_incoming_notification(LinphoneCall
LINPHONE_PUBLIC LinphoneCall *linphone_call_new_incoming_with_callid(LinphoneCore *lc, const char *callid);
LINPHONE_PUBLIC bool_t linphone_call_is_op_configured (const LinphoneCall *call);
void _linphone_core_stop_async_end(LinphoneCore *lc);
void _linphone_core_uninit(LinphoneCore *lc);
void linphone_core_write_auth_info(LinphoneCore *lc, LinphoneAuthInfo *ai);
const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc);
......
......@@ -201,6 +201,8 @@ private:
unsigned long pushReceivedBackgroundTaskId;
std::list<AudioDevice *> audioDevices;
bool stopAsyncEndEnabled = false;
ExtraBackgroundTask bgTask {"Stop core async end"};
L_DECLARE_PUBLIC(Core);
};
......
......@@ -161,6 +161,25 @@ void CorePrivate::unregisterListener (CoreListener *listener) {
listeners.remove(listener);
}
void Core::onStopAsyncBackgroundTaskStarted() {
L_D();
d->stopAsyncEndEnabled = false;
function<void()> stopAsyncEnd = [d]() {
_linphone_core_stop_async_end(d->getCCore());
};
function<void()> enableStopAsyncEnd = [d]() {
d->stopAsyncEndEnabled = true;
};
d->bgTask.start(getSharedFromThis(), stopAsyncEnd, enableStopAsyncEnd, linphone_config_get_int(linphone_core_get_config(getCCore()), "misc", "max_stop_async_time", 10));
}
void Core::onStopAsyncBackgroundTaskStopped() {
L_D();
d->bgTask.stop();
}
// Called by linphone_core_iterate() to check that aynchronous tasks are done.
// It is used to give a chance to end asynchronous tasks during core stop
// or to make sure that asynchronous tasks are finished during an aynchronous core stop.
......@@ -179,6 +198,13 @@ bool CorePrivate::isShutdownDone() {
}
}
// Sometimes (bad network for example), backgrounds for chat message (imdn, delivery ...) take too much time.
// In this case, forece linphonecore to off. Using "stop core async end" background task to do this.
if (stopAsyncEndEnabled) {
return true;
}
for (auto it = chatRoomsById.begin(); it != chatRoomsById.end(); it++) {
const auto &chatRoom = dynamic_pointer_cast<ChatRoom>(it->second);
if (chatRoom && (chatRoom->getPrivate()->getImdnHandler()->hasUndeliveredImdnMessage()
......
......@@ -256,6 +256,8 @@ public:
/* Stop (ie cancel) and destroy a timer created by createTimer()*/
void destroyTimer(belle_sip_source_t *timer);
void onStopAsyncBackgroundTaskStarted(); /* Using background task to ensure stop core async ended */
void onStopAsyncBackgroundTaskStopped();
private:
Core ();
......
......@@ -85,4 +85,22 @@ void BackgroundTask::handleTimeout () {
stop();
}
void ExtraBackgroundTask::start (const shared_ptr<Core> &core, const std::function<void ()> &extraFunc, const std::function<void ()> &extraSalFunc, int maxDurationSeconds) {
sExtraFunc = extraFunc;
sExtraSalFunc = extraSalFunc;
BackgroundTask::start(core, maxDurationSeconds);
}
void ExtraBackgroundTask::handleTimeout() {
BackgroundTask::handleTimeout();
sExtraFunc();
}
void ExtraBackgroundTask::handleSalTimeout() {
BackgroundTask::handleSalTimeout();
sExtraSalFunc();
}
LINPHONE_END_NAMESPACE
......@@ -50,11 +50,11 @@ public:
protected:
virtual void handleTimeout ();
virtual void handleSalTimeout();
private:
static int sHandleSalTimeout(void *data, unsigned int events);
static void sHandleTimeout(void *data);
void handleSalTimeout();
belle_sip_source_t *mTimeout = nullptr;
Sal *mSal = nullptr;
......@@ -62,6 +62,22 @@ private:
unsigned long mId = 0;
};
class ExtraBackgroundTask: public BackgroundTask {
public:
ExtraBackgroundTask (const std::string &name) : BackgroundTask(name) {}
~ExtraBackgroundTask () = default;
void start (const std::shared_ptr<Core> &core, const std::function<void ()> &extraFunc, const std::function<void ()> &extraSalFunc, int maxDurationSeconds = 15 * 60);
protected:
void handleTimeout() override;
void handleSalTimeout() override;
private:
std::function<void()> sExtraFunc;
std::function<void()> sExtraSalFunc;
};
LINPHONE_END_NAMESPACE
#endif // ifndef _L_BACKGROUND_TASK_H_
......@@ -470,6 +470,49 @@ static void two_shared_executor_cores_get_message_and_chat_room(void) {
#endif
}
static void stop_async_core_when_belle_sip_task_failed(void) {
#if TARGET_OS_IPHONE
if (!linphone_factory_is_database_storage_available(linphone_factory_get())) {
ms_warning("Test skipped, database storage is not available");
return;
}
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager *pauline = linphone_core_manager_create2("pauline_tcp_rc", NULL);
// simulate bad net work to trash the message without generating error
linphone_config_set_bool(linphone_core_get_config(pauline->lc), "net", "bad_net", 1);
linphone_core_manager_start(pauline, TRUE);
linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie->lc));
linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline->lc));
LinphoneChatRoom* chat_room = linphone_core_get_chat_room(marie->lc, pauline->identity);
LinphoneChatMessage* msg = linphone_chat_room_create_message_from_utf8(chat_room,"Bli bli bli \n blu");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg);
BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageSent, 0, int, "%d");
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
linphone_chat_message_send(msg);
char *message_id = ms_strdup(linphone_chat_message_get_message_id(msg));
BC_ASSERT_STRING_NOT_EQUAL(message_id, "");
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneMessageReceived, 1));
linphone_core_stop_async(pauline->lc);
BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneMessageDelivered,0));
BC_ASSERT_TRUE(wait_for_until(pauline->lc, NULL, &pauline->stat.number_of_LinphoneGlobalOff, pauline->stat.number_of_LinphoneGlobalOff+1, 30000));
ms_free(message_id);
linphone_chat_message_unref(msg);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
#endif
}
test_t shared_core_tests[] = {
TEST_NO_TAG("Executor Shared Core can't start because Main Shared Core runs", shared_main_core_prevent_executor_core_start),
TEST_NO_TAG("Executor Shared Core stopped by Main Shared Core", shared_main_core_stops_executor_core),
......@@ -478,7 +521,8 @@ test_t shared_core_tests[] = {
TEST_NO_TAG("Executor Shared Core get message from callId with user defaults on two threads", shared_executor_core_get_message_with_user_defaults_multi_thread),
TEST_NO_TAG("Two Executor Shared Cores get messages", two_shared_executor_cores_get_messages),
TEST_NO_TAG("Executor Shared Core get new chat room from invite", shared_executor_core_get_chat_room),
TEST_NO_TAG("Two Executor Shared Cores get one msg and one chat room", two_shared_executor_cores_get_message_and_chat_room)
TEST_NO_TAG("Two Executor Shared Cores get one msg and one chat room", two_shared_executor_cores_get_message_and_chat_room),
TEST_NO_TAG("stop async core when belle sip task failed", stop_async_core_when_belle_sip_task_failed)
};
void shared_core_tester_before_each(void) {
......
......@@ -2085,6 +2085,10 @@ void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMess
message_external_body_url=NULL;
}
}
if (linphone_config_get_bool(linphone_core_get_config(lc), "net", "bad_net", 0)) {
sal_set_send_error(linphone_core_get_sal(lc), 1500);
}
}
void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment