Assure proper termination using context session 66/108966/5 accepted/tizen_3.0.m2_common accepted/tizen_3.0.m2_mobile accepted/tizen_3.0.m2_tv accepted/tizen_3.0.m2_wearable tizen_3.0.m2 accepted/tizen/3.0.m2/common/20170111.094130 accepted/tizen/3.0.m2/mobile/20170111.043120 accepted/tizen/3.0.m2/tv/20170111.043138 accepted/tizen/3.0.m2/wearable/20170111.043201 accepted/tizen/3.0/common/20170110.125811 accepted/tizen/3.0/ivi/20170110.044431 accepted/tizen/3.0/mobile/20170110.044338 accepted/tizen/3.0/tv/20170110.044355 accepted/tizen/3.0/wearable/20170110.044412 submit/tizen_3.0.m2/20170109.152207 submit/tizen_3.0.m2/20170111.004805 submit/tizen_3.0/20170109.151907
authorYoungsoo Choi <kenshin.choi@samsung.com>
Wed, 4 Jan 2017 09:01:10 +0000 (18:01 +0900)
committerYoungsoo Choi <kenshin.choi@samsung.com>
Mon, 9 Jan 2017 15:11:11 +0000 (00:11 +0900)
Current main loop quits when closing window callback is called.
Btw, it does not assure proper termination of changing watch face
because context session stops too early and callback can not be called
and then dead lock happens.

So, this CL extends the main loop until all the context sessions are finished
and covers following termination scenarios.

* h/w back key
* exit pop up menu
* exit command |pkgcmd -k|
* uninstall command |pkgcmd -u| while webapp runs
* changing watch face

Bug: http://suprem.sec.samsung.net/jira/browse/TWF-2765

Change-Id: I1767ef5f14e393ffc59668b0b9925d781abfd2f5
Signed-off-by: Youngsoo Choi <kenshin.choi@samsung.com>
extensions/renderer/xwalk_extension_renderer_controller.cc
extensions/renderer/xwalk_extension_renderer_controller.h
runtime/browser/runtime_process.cc
runtime/browser/web_application.cc
runtime/browser/web_application.h

index 59e45b7..01c1ece 100755 (executable)
@@ -22,6 +22,9 @@
 
 namespace extensions {
 
+// static
+int XWalkExtensionRendererController::plugin_session_count = 0;
+
 namespace {
 
 void CreateExtensionModules(XWalkExtensionClient* client,
@@ -81,12 +84,16 @@ void XWalkExtensionRendererController::DidCreateScriptContext(
   CreateExtensionModules(extensions_client_.get(), module_system);
 
   module_system->Initialize();
+  plugin_session_count++;
+  LOGGER(DEBUG) << "plugin_session_count : " << plugin_session_count;
 }
 
 void XWalkExtensionRendererController::WillReleaseScriptContext(
     v8::Handle<v8::Context> context) {
   v8::Context::Scope contextScope(context);
   XWalkModuleSystem::ResetModuleSystemFromContext(context);
+  plugin_session_count--;
+  LOGGER(DEBUG) << "plugin_session_count : " << plugin_session_count;
 }
 
 void XWalkExtensionRendererController::OnReceivedIPCMessage(
index 4927d21..95e3145 100755 (executable)
@@ -20,6 +20,7 @@ class XWalkExtensionClient;
 class XWalkExtensionRendererController {
  public:
   static XWalkExtensionRendererController& GetInstance();
+  static int plugin_session_count;
 
   void DidCreateScriptContext(v8::Handle<v8::Context> context);
   void WillReleaseScriptContext(v8::Handle<v8::Context> context);
index 859aa3a..83d7645 100755 (executable)
@@ -28,6 +28,7 @@
 #include "common/command_line.h"
 #include "common/logger.h"
 #include "common/profiler.h"
+#include "extensions/renderer/xwalk_extension_renderer_controller.h"
 #include "runtime/browser/runtime.h"
 #include "runtime/common/constants.h"
 #include "runtime/browser/prelauncher.h"
@@ -35,6 +36,7 @@
 
 #include "runtime/browser/ui_runtime.h"
 
+using namespace extensions;
 bool g_prelaunch = false;
 
 #ifdef WATCH_FACE_FEATURE_SUPPORT
@@ -132,16 +134,22 @@ int real_main(int argc, char* argv[]) {
     std::unique_ptr<runtime::Runtime> runtime =
         runtime::Runtime::MakeRuntime(appdata);
     ret = runtime->Exec(argc, argv);
-    if (runtime->is_on_terminate_called) {
-      LOGGER(INFO) << "Defer termination of main loop";
+    if (ret)
+      LOGGER(ERROR) << "Exec returns non zero.";
+    LOGGER(DEBUG) << "plugin_session_count : " <<
+        XWalkExtensionRendererController::plugin_session_count;
+    if (XWalkExtensionRendererController::plugin_session_count > 0) {
+      LOGGER(DEBUG) << "Defer termination of main loop";
       ecore_main_loop_begin();
     }
     runtime.reset();
   }
+  LOGGER(DEBUG) << "ewk_shutdown";
   ewk_shutdown();
   elm_shutdown();
   elm_exit();
 
+  LOGGER(DEBUG) << "EXIT_SUCCESS";
   return EXIT_SUCCESS;
 }
 
index 9a5d50b..68143a8 100755 (executable)
@@ -37,6 +37,7 @@
 #include "common/profiler.h"
 #include "common/resource_manager.h"
 #include "common/string_utils.h"
+#include "extensions/renderer/xwalk_extension_renderer_controller.h"
 #include "runtime/browser/native_window.h"
 #include "runtime/browser/notification_manager.h"
 #include "runtime/browser/popup.h"
 #error INJECTED_BUNDLE_PATH is not set.
 #endif
 
+#define TIMER_INTERVAL 0.1
+
+using namespace extensions;
+
 namespace runtime {
 
 namespace {
@@ -222,8 +227,10 @@ static void InitializeNotificationCallback(Ewk_Context* ewk_context,
 static Eina_Bool ExitAppIdlerCallback(void* data) {
   WebApplication* app = static_cast<WebApplication*>(data);
 
-  if (app)
+  if (app) {
+    LOGGER(DEBUG) << "Terminate";
     app->Terminate();
+  }
 
   return ECORE_CALLBACK_CANCEL;
 }
@@ -285,6 +292,7 @@ WebApplication::WebApplication(
       debug_mode_(false),
       verbose_mode_(false),
       lang_changed_mode_(false),
+      is_terminate_called_(false),
       ewk_context_(
           ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
       has_ownership_of_ewk_context_(true),
@@ -303,6 +311,7 @@ WebApplication::WebApplication(
     : launched_(false),
       debug_mode_(false),
       verbose_mode_(false),
+      is_terminate_called_(false),
       ewk_context_(context),
       has_ownership_of_ewk_context_(false),
       window_(window),
@@ -637,23 +646,28 @@ void WebApplication::Suspend() {
 }
 
 void WebApplication::Terminate() {
+  is_terminate_called_ = true;
   if (terminator_) {
+    LOGGER(DEBUG) << "terminator_";
     terminator_();
   } else {
+    LOGGER(ERROR) << "There's no registered terminator.";
     elm_exit();
   }
   auto extension_server = extensions::XWalkExtensionServer::GetInstance();
-  LOGGER(INFO) << "Shutdown extension server";
+  LOGGER(DEBUG) << "Shutdown extension server";
   extension_server->Shutdown();
 }
 
 void WebApplication::ClosePageFromOnTerminate() {
+  LOGGER(DEBUG);
   auto it = view_stack_.begin();
   if (it != view_stack_.end()) {
     runtime::Runtime::is_on_terminate_called = true;
     for (; it != view_stack_.end(); ++it) {
       (*it)->ReplyToJavascriptDialog();
       view_stack_.front()->SetVisibility(false);
+      LOGGER(DEBUG) << "ewk_view_page_close";
       ewk_view_page_close((*it)->evas_object());
     }
   }
@@ -692,7 +706,13 @@ void WebApplication::RemoveWebViewFromStack(WebView* view) {
   }
 
   if (view_stack_.size() == 0) {
-    Terminate();
+    // If |Terminate()| hasn't been called,
+    // main loop shouldn't be terminated here.
+    if (!is_terminate_called_) {
+      auto extension_server = XWalkExtensionServer::GetInstance();
+      LOGGER(DEBUG) << "Shutdown extension server";
+      extension_server->Shutdown();
+    }
   } else if (current != view_stack_.front()) {
     view_stack_.front()->SetVisibility(true);
     window_->SetContent(view_stack_.front()->evas_object());
@@ -707,14 +727,43 @@ void WebApplication::RemoveWebViewFromStack(WebView* view) {
                   view);
 }
 
+Eina_Bool WebApplication::CheckPluginSession(void* user_data)
+{
+  WebApplication* that = static_cast<WebApplication*>(user_data);
+  if(XWalkExtensionRendererController::plugin_session_count > 0) {
+    LOGGER(ERROR) << "plugin_session_count : " <<
+        XWalkExtensionRendererController::plugin_session_count;
+    return ECORE_CALLBACK_RENEW;
+  }
+  LOGGER(DEBUG) << "plugin_session_count : " <<
+      XWalkExtensionRendererController::plugin_session_count;
+  LOGGER(DEBUG) << "Execute deferred termination of main loop";
+  if (that->is_terminate_called_) {
+    ecore_main_loop_quit();
+  } else {
+    if (that->terminator_) {
+      LOGGER(DEBUG) << "terminator_";
+      that->terminator_();
+    } else {
+      LOGGER(ERROR) << "There's no registered terminator.";
+      elm_exit();
+    }
+  }
+  return ECORE_CALLBACK_CANCEL;
+}
+
 void WebApplication::OnClosedWebView(WebView* view) {
+  Ecore_Timer* timeout_id = NULL;
   // Reply to javascript dialog for preventing freeze issue.
   view->ReplyToJavascriptDialog();
   RemoveWebViewFromStack(view);
 
-  if (runtime::Runtime::is_on_terminate_called) {
-    LOGGER(INFO) << "Execute deferred termination of main loop";
-    ecore_main_loop_quit();
+  LOGGER(DEBUG) << "plugin_session_count : " <<
+      XWalkExtensionRendererController::plugin_session_count;
+  if (XWalkExtensionRendererController::plugin_session_count > 0) {
+    timeout_id = ecore_timer_add(TIMER_INTERVAL, CheckPluginSession, this);
+    if (!timeout_id)
+      LOGGER(ERROR) << "It's failed to create timer";
   }
 }
 
@@ -831,6 +880,7 @@ void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
       if(enabled)
         view->EvalJavascript(kBackKeyEventScript);
       if (!view->Backward()) {
+        LOGGER(DEBUG) << "Terminate";
         Terminate();
       }
     }
@@ -846,6 +896,7 @@ void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
         (app_data_->widget_info() != NULL &&
          app_data_->widget_info()->view_modes() == "windowed")) {
       if (!view->Backward()) {
+        LOGGER(DEBUG) << "Terminate";
         Terminate();
       }
     }
index 751e809..48665e8 100755 (executable)
@@ -106,6 +106,7 @@ class WebApplication : public WebView::EventListener {
 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
   virtual void OnRotatePrepared(WebView* view);
 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
+  static Eina_Bool CheckPluginSession(void* user_data);
 
  private:
   bool Initialize();
@@ -130,6 +131,7 @@ class WebApplication : public WebView::EventListener {
   bool debug_mode_;
   bool verbose_mode_;
   bool lang_changed_mode_;
+  bool is_terminate_called_;
   Ewk_Context* ewk_context_;
   bool has_ownership_of_ewk_context_;
   NativeWindow* window_;