[Sensor] fixing listener behaviours 77/151677/2 accepted/tizen_3.0_ivi accepted/tizen/3.0/common/20170929.203111 accepted/tizen/3.0/ivi/20170922.023214 accepted/tizen/3.0/mobile/20170922.023152 accepted/tizen/3.0/tv/20170922.023143 accepted/tizen/3.0/wearable/20170922.023113 submit/tizen_3.0/20170921.132449
authorPiotr Kosko <p.kosko@samsung.com>
Thu, 21 Sep 2017 10:13:10 +0000 (12:13 +0200)
committerPiotr Kosko <p.kosko@samsung.com>
Thu, 21 Sep 2017 12:50:59 +0000 (14:50 +0200)
[Bug] Listeners are not called in deterministic way,
  several "api calls order" issues are fixed.

[Verification] Code compiles properly
  TCT passrate 100% - auto & manual
  Web widget service tests of sensor - 119 pass - 100% passrate

Change-Id: I720e23e0f23a9a849fb2d507e8721f196578ac48
Signed-off-by: Piotr Kosko <p.kosko@samsung.com>
src/sensor/sensor_api.js
src/sensor/sensor_service.cc

index 7a68684a4ad324559afe4f8b3e2effad7ff10019..8c34e338fceb8117859d09e4779600037aa52bcf 100755 (executable)
@@ -49,10 +49,17 @@ var MagneticSensorAccuracy = {
     VERYGOOD : 'ACCURACY_VERYGOOD'
 };
 
+var SensorStates = {
+    NOT_STARTED : 0,
+    STARTING : 1,
+    STARTED : 2
+};
+
+
 // helper class for sensor listeners
 var SensorListener = function (type, constructor) {
     this.sensorType = type;
-    this.isStarted = false;
+    this.state = SensorStates.NOT_STARTED;
     this.callback = undefined;
     this.constructor = constructor;
 };
@@ -64,8 +71,9 @@ SensorListener.prototype.tryCall = function (object) {
 };
 
 SensorListener.prototype.start = function (successCallback, errorCallback) {
-    if (!this.isStarted) {
+    if (SensorStates.STARTED != this.state) {
         // sensor not started
+        this.state = SensorStates.STARTING;
         var thisObject = this;
         native_.call('Sensor_start', {'sensorType' : thisObject.sensorType},
                 function(result) {
@@ -74,7 +82,7 @@ SensorListener.prototype.start = function (successCallback, errorCallback) {
                             errorCallback(native_.getErrorObject(result));
                         }
                     } else {
-                        thisObject.isStarted = true;
+                        thisObject.state = SensorStates.STARTED;
                         successCallback();
                     }
                 }
@@ -86,12 +94,12 @@ SensorListener.prototype.start = function (successCallback, errorCallback) {
 };
 
 SensorListener.prototype.stop = function () {
-    if (this.isStarted) {
+    if (SensorStates.NOT_STARTED != this.state) {
         var result = native_.callSync('Sensor_stop', {'sensorType' : this.sensorType});
         if (native_.isFailure(result)) {
             throw native_.getErrorObject(result);
         }
-        this.isStarted = false;
+        this.state = SensorStates.NOT_STARTED;
     }
 };
 
@@ -123,7 +131,7 @@ SensorListener.prototype.unsetListener = function () {
 
 SensorListener.prototype.getData = function (successCallback, errorCallback) {
     var thisObj = this;
-    if (!thisObj.isStarted) {
+    if (SensorStates.STARTED != this.state) {
         setTimeout(function() {
             if (!T_.isNullOrUndefined(errorCallback)) {
                 errorCallback(new WebAPIException(
index 71a3ab25c8f5131f2364aff85c73b28391c2e292..1596ba6845f7d394d57d9653335a7044b33acf98 100755 (executable)
@@ -219,6 +219,7 @@ class SensorData {
 
  private:
   static void SensorCallback(sensor_h sensor, sensor_event_s* event, void* user_data);
+  PlatformResult AddDelayedStartSuccessCb(const std::function<void()>& successCb);
 
   sensor_type_e type_enum_;
   EventComparator comparator_;
@@ -229,6 +230,8 @@ class SensorData {
   common::optional<bool> is_supported_;
   SensorInstance& instance_;
   std::mutex initialization_mutex_;
+  bool is_change_listener_set_;
+  std::vector<std::function<void()>> delayed_success_callbacks_;
 };
 
 SensorData::SensorData(SensorInstance& instance, sensor_type_e type_enum,
@@ -239,8 +242,10 @@ SensorData::SensorData(SensorInstance& instance, sensor_type_e type_enum,
       sensor_value_count_(sensor_value_count),
       handle_(nullptr),
       listener_(nullptr),
-      previous_event_(),
-      instance_(instance) {
+      previous_event_{0,0,0,-FLT_MAX},  // setting dumb non-zero value to differ init value from
+                                        // "good" zero values from sensor
+      instance_(instance),
+      is_change_listener_set_(false) {
   type_to_string_map.insert(std::make_pair(type_enum, name));
   string_to_type_map.insert(std::make_pair(name, type_enum));
 
@@ -267,6 +272,23 @@ void SensorData::SensorCallback(sensor_h sensor, sensor_event_s* event, void* us
 
   LoggerD("Entered: %s", type_to_string_map[that->type()].c_str());
 
+  if (!that->delayed_success_callbacks_.empty()) {
+    for_each(that->delayed_success_callbacks_.begin(), that->delayed_success_callbacks_.end(),
+             [](std::function<void()>& callback) {
+      LoggerD("Calling delayed start succcess callback");
+      callback();
+    });
+    that->delayed_success_callbacks_.erase(that->delayed_success_callbacks_.begin(),
+                                           that->delayed_success_callbacks_.end());
+    if (!that->is_change_listener_set_) {
+      LoggerD("unregistering temporary listener used to delay start success callback");
+      int ret = sensor_listener_unset_event_cb(that->listener_);
+      if (SENSOR_ERROR_NONE != ret) {
+        LoggerE("unsetting temporary listener failed: %d", ret);
+      }
+    }
+  }
+
   if (!that->UpdateEvent(event)) {
     // value didn't change - ignore
     return;
@@ -361,16 +383,23 @@ bool SensorData::UpdateEvent(sensor_event_s* event) {
   }
 }
 
-struct DelaySuccessCbData {
-  DelaySuccessCbData(const std::shared_ptr<picojson::value> &_result)
-      : result(_result),
-        delay_success_cb_listener(nullptr) {
-  }
+PlatformResult SensorData::AddDelayedStartSuccessCb(const std::function<void()>& successCb) {
+  LoggerD("Entered");
+  delayed_success_callbacks_.push_back(successCb);
 
-  const std::shared_ptr<picojson::value> result;
-  sensor_listener_h delay_success_cb_listener;
-  std::function<void(const std::shared_ptr<picojson::value>&)> report_result;
-};
+  if (!is_change_listener_set_) {
+    LoggerD("Adding temporary listener by hand");
+    int ret = sensor_listener_set_event_cb(listener_, 10, // as small interval as possible for tmp listener
+                                           SensorCallback, this);
+    if (SENSOR_ERROR_NONE != ret) {
+      LoggerE("sensor_listener_set_event_cb returned error: %d", ret);
+      return GetSensorPlatformResult(ret, "sensor_listener_set_event_cb");
+    }
+  } else {
+    LoggerD("Temporary listener is not needed");
+  }
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
 
 PlatformResult SensorData::Start(const std::shared_ptr<picojson::value>& result,
                                  const std::function<void(const std::shared_ptr<picojson::value>&)>& report_result) {
@@ -382,75 +411,19 @@ PlatformResult SensorData::Start(const std::shared_ptr<picojson::value>& result,
     return res;
   }
 
-  sensor_listener_h delay_success_cb_listener = nullptr;
-  int ret = sensor_create_listener(handle_, &delay_success_cb_listener);
-  if (SENSOR_ERROR_NONE != ret) {
-    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
-                              "Failed to create listener.",
-                              ("sensor_create_listener() error: %d, message: %s", ret, get_error_message(ret)));
-  }
-
-  ret = sensor_listener_set_option(delay_success_cb_listener, SENSOR_OPTION_ALWAYS_ON);
-  if (SENSOR_ERROR_NONE != ret) {
-    sensor_destroy_listener(delay_success_cb_listener);
-    LoggerE("sensor_listener_set_option : %d", ret);
-    return GetSensorPlatformResult(ret, "sensor_listener_set_option");
-  }
-
-  DelaySuccessCbData *data = new DelaySuccessCbData(result);
-  data->delay_success_cb_listener = delay_success_cb_listener;
-  data->report_result = report_result;
-
-  ret = sensor_listener_set_event_cb(delay_success_cb_listener, 10,//as less as possible
-      [](sensor_h sensor, sensor_event_s* event, void* user_data) -> void {
-    LoggerD("Enter");
-    DelaySuccessCbData *data = static_cast<DelaySuccessCbData*>(user_data);
-    if (nullptr == data) {
-      LoggerE("data equal to nullptr");
-      return;
-    }
-
-    SCOPE_EXIT {
-      delete data;
-    };
-
-    sensor_listener_h listener = data->delay_success_cb_listener;
-    ReportSuccess(data->result->get<picojson::object>());
-    data->report_result(data->result);
-
-    int ret = sensor_listener_unset_event_cb(listener);
-    if (SENSOR_ERROR_NONE != ret) {
-      LoggerE("sensor_listener_unset_event_cb : %d", ret);
-    }
-
-    ret = sensor_destroy_listener(listener);
-    if (SENSOR_ERROR_NONE != ret) {
-      LoggerE("sensor_listener_unset_event_cb : %d", ret);
-    }
-  }, data);
-
-  if (SENSOR_ERROR_NONE != ret) {
-    sensor_destroy_listener(delay_success_cb_listener);
-    delete data;
-    LoggerE("sensor_listener_set_event_cb : %d", ret);
-    return GetSensorPlatformResult(ret, "sensor_listener_set_event_cb");
-  }
-
-  ret = sensor_listener_start(delay_success_cb_listener);
-  if (SENSOR_ERROR_NONE != ret) {
-    sensor_listener_unset_event_cb(delay_success_cb_listener);
-    sensor_destroy_listener(delay_success_cb_listener);
-    delete data;
-    LoggerE("sensor_listener_start : %d", ret);
-    return GetSensorPlatformResult(ret, "sensor_listener_start");
+  auto delayed_success_callback = [this, result, report_result] () {
+    LoggerD("Delayed success callback");
+    ReportSuccess(result->get<picojson::object>());
+    report_result(result);
+  };
+  res = AddDelayedStartSuccessCb(delayed_success_callback);
+  if(!res) {
+    return res;
   }
 
   sensor_listener_set_option(listener_, SENSOR_OPTION_ALWAYS_ON);
-  ret = sensor_listener_start(listener_);
+  int ret = sensor_listener_start(listener_);
   if (SENSOR_ERROR_NONE != ret) {
-    sensor_listener_unset_event_cb(delay_success_cb_listener);
-    sensor_destroy_listener(delay_success_cb_listener);
-    delete data;
     LoggerE("sensor_listener_start : %d", ret);
     return GetSensorPlatformResult(ret, "sensor_listener_start");
   }
@@ -474,6 +447,12 @@ PlatformResult SensorData::Stop() {
     return GetSensorPlatformResult(ret, "sensor_listener_stop");
   }
 
+  // reseting delayed success callbacks flag and saved event values
+  previous_event_ = {0,0,0,-FLT_MAX};  // setting dumb non-zero value to differ init value from
+                                       // "good" zero values from sensor
+  delayed_success_callbacks_.erase(delayed_success_callbacks_.begin(),
+                                   delayed_success_callbacks_.end());
+
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
@@ -502,6 +481,8 @@ PlatformResult SensorData::SetChangeListener(unsigned int interval, unsigned int
     return GetSensorPlatformResult(ret, "sensor_listener_set_event_cb");
   }
 
+  is_change_listener_set_ = true;
+
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
@@ -521,6 +502,8 @@ PlatformResult SensorData::UnsetChangeListener() {
     return GetSensorPlatformResult(ret, "sensor_listener_unset_event_cb");
   }
 
+  is_change_listener_set_ = false;
+
   return PlatformResult(ErrorCode::NO_ERROR);
 }