DatacontrolInstance::~DatacontrolInstance() {
ScopeLogger();
+
+ std::lock_guard<std::mutex> lock{reply_map_mutex};
for (auto& item : reply_map) {
int watch_id = item.first;
auto handle = item.second->handle;
// According to native documentation only IOError can be returned to webapi, other errors are
// handled earlier
LogAndReportError(IOException("DataControlConsumerObjectAddChangeListener failed"), obj);
+
Instance::PostMessage(data->_instance, event.serialize().c_str());
if (0 != watch_id) {
- data->_instance->EraseMap(watch_id);
+ std::lock_guard<std::mutex> reply_map_lock{data->_instance->reply_map_mutex};
+ data->_instance->reply_map.erase(watch_id);
}
}
}
user_data->handle = handle;
int watch_id = 0;
- result = ::data_control_add_data_change_cb(handle, callback, user_data.get(), result_callback,
- user_data.get(), &watch_id);
+ {
+ /*
+ * "result_callback", which manipulates reply_map, can be called before or
+ * simultaneously with insertion of the watch_id to reply_map, which happens
+ * after checking result code.
+ * To prevent a race condition in access to reply_map, we lock its mutex before calling
+ * data_control_add_data_change_cb.
+ */
+ std::lock_guard<std::mutex> lock{reply_map_mutex};
+ result = ::data_control_add_data_change_cb(handle, callback, user_data.get(), result_callback,
+ user_data.get(), &watch_id);
- if (DATA_CONTROL_ERROR_NONE != result) {
- LogAndReportError(
- ServiceNotAvailableException("DataControlConsumerObjectAddChangeListener failed"), out,
- ("DataControlConsumerObjectAddChangeListener failed: %d (%s)", result,
- get_error_message(result)));
- return;
- }
+ if (DATA_CONTROL_ERROR_NONE != result) {
+ LogAndReportError(
+ ServiceNotAvailableException("DataControlConsumerObjectAddChangeListener failed"), out,
+ ("DataControlConsumerObjectAddChangeListener failed: %d (%s)", result,
+ get_error_message(result)));
+ return;
+ }
- reply_map.insert(std::pair<int, ReplyCallbackDataPtr>(watch_id, user_data));
+ reply_map.insert(std::pair<int, ReplyCallbackDataPtr>(watch_id, user_data));
+ }
picojson::value return_value = picojson::value(picojson::object());
picojson::object& return_value_obj = return_value.get<picojson::object>();
CHECK_EXIST(args, "watchId", out)
int watch_id = static_cast<int>(args.get("watchId").get<double>());
+
+ std::lock_guard<std::mutex> lock{reply_map_mutex};
if (reply_map.end() == reply_map.find(watch_id)) {
LogAndReportError(
IOException("DataControlConsumerObjectRemoveChangeListener failed"), out,
int CreateSQLHandle(const std::string& providerId, const std::string& dataId,
data_control_h* handle);
-
- void EraseMap(int watch_id) {
- reply_map.erase(watch_id);
- };
-
static void callback(data_control_h provider, data_control_data_change_type_e type,
bundle* bundle_data, void* user_data);
static void result_callback(data_control_h provider, data_control_error_e result, int callback_id,
void DataControlConsumerObjectRemoveChangeListener(const picojson::value& args,
picojson::object& out);
+ /*
+ * reply_map can be accessed in the thread, in which Web API calls are run
+ * and in callbacks which may run in in other threads. Thus we need a mutex to protect
+ * it from race conditions.
+ */
+ std::mutex reply_map_mutex;
ReplyCallbackDataMap reply_map;
};