From: Pawel Wasowski Date: Wed, 21 Oct 2020 03:15:01 +0000 (+0200) Subject: [Datacontrol] Prevent possible race conditions X-Git-Tag: submit/tizen/20201104.133613~2^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b5fdee72497c8ca15728d626e3ba377a4789e49c;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Datacontrol] Prevent possible race conditions DataconstrolInstance::reply_map is accessed in callbacks that can run in threads other than that, which runs API calls. This commit adds a mutex and proper locks to prevent race conditions. [Verification] tct-datacontrol-tizen-tests: 100% pass rate Change-Id: I2573a59c36af3e12a0d5e77598b170192d24f225 Signed-off-by: Pawel Wasowski --- diff --git a/src/datacontrol/datacontrol_instance.cc b/src/datacontrol/datacontrol_instance.cc index a7694c57..d954fa75 100644 --- a/src/datacontrol/datacontrol_instance.cc +++ b/src/datacontrol/datacontrol_instance.cc @@ -109,6 +109,8 @@ DatacontrolInstance::DatacontrolInstance() { DatacontrolInstance::~DatacontrolInstance() { ScopeLogger(); + + std::lock_guard lock{reply_map_mutex}; for (auto& item : reply_map) { int watch_id = item.first; auto handle = item.second->handle; @@ -1154,9 +1156,11 @@ void DatacontrolInstance::result_callback(data_control_h provider, data_control_ // 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 reply_map_lock{data->_instance->reply_map_mutex}; + data->_instance->reply_map.erase(watch_id); } } } @@ -1201,18 +1205,28 @@ void DatacontrolInstance::DataControlConsumerObjectAddChangeListener(const picoj 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 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(watch_id, user_data)); + reply_map.insert(std::pair(watch_id, user_data)); + } picojson::value return_value = picojson::value(picojson::object()); picojson::object& return_value_obj = return_value.get(); @@ -1231,6 +1245,8 @@ void DatacontrolInstance::DataControlConsumerObjectRemoveChangeListener(const pi CHECK_EXIST(args, "watchId", out) int watch_id = static_cast(args.get("watchId").get()); + + std::lock_guard lock{reply_map_mutex}; if (reply_map.end() == reply_map.find(watch_id)) { LogAndReportError( IOException("DataControlConsumerObjectRemoveChangeListener failed"), out, diff --git a/src/datacontrol/datacontrol_instance.h b/src/datacontrol/datacontrol_instance.h index cb639f04..8ad514a4 100644 --- a/src/datacontrol/datacontrol_instance.h +++ b/src/datacontrol/datacontrol_instance.h @@ -57,11 +57,6 @@ class DatacontrolInstance : public common::ParsedInstance { 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, @@ -83,6 +78,12 @@ class DatacontrolInstance : public common::ParsedInstance { 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; };