From 1cf844ee5b172264bb02a9a4bb5b591ccd1ff8a0 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 22 Mar 2021 15:17:25 -0400 Subject: [PATCH 1/2] Port cl/364325997: Fix `Mutex` objects being incorrectly run at exit, which can cause crashes when `exit()` is called. See https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables for rationale and https://github.com/firebase/firebase-ios-sdk/pull/6849 for a similar fix that was made in the iOS client. --- app/rest/transport_curl.cc | 6 +++--- app/rest/util.cc | 10 +++++----- app/src/app_common.cc | 24 ++++++++++++------------ app/src/callback.cc | 12 ++++++------ app/src/cleanup_notifier.cc | 16 ++++++++-------- app/src/cleanup_notifier.h | 2 +- app/src/secure/user_secure_manager.cc | 6 +++--- app/src/secure/user_secure_manager.h | 2 +- app/src/util.cc | 18 +++++++++--------- app/src/util.h | 4 ++-- auth/src/auth.cc | 8 ++++---- auth/src/desktop/rpcs/auth_request.cc | 4 ++-- firestore/src/common/firestore.cc | 8 ++++---- 13 files changed, 60 insertions(+), 60 deletions(-) diff --git a/app/rest/transport_curl.cc b/app/rest/transport_curl.cc index ac5bc7a8dd..730be14da6 100644 --- a/app/rest/transport_curl.cc +++ b/app/rest/transport_curl.cc @@ -312,12 +312,12 @@ CurlThread* g_curl_thread = nullptr; int g_initialize_count = 0; // Mutex for Curl initialization. -Mutex g_initialize_mutex; // NOLINT +Mutex* g_initialize_mutex = new Mutex(); } // namespace void InitTransportCurl() { - MutexLock lock(g_initialize_mutex); + MutexLock lock(*g_initialize_mutex); if (g_initialize_count == 0) { // Initialize curl. CURLcode global_init_code = curl_global_init(CURL_GLOBAL_ALL); @@ -333,7 +333,7 @@ void InitTransportCurl() { } void CleanupTransportCurl() { - MutexLock lock(g_initialize_mutex); + MutexLock lock(*g_initialize_mutex); assert(g_initialize_count > 0); g_initialize_count--; if (g_initialize_count == 0) { diff --git a/app/rest/util.cc b/app/rest/util.cc index 85a581945c..e2dbc8a078 100644 --- a/app/rest/util.cc +++ b/app/rest/util.cc @@ -45,23 +45,23 @@ const char kGet[] = "GET"; const char kPost[] = "POST"; // Mutex for utils initialization and distribution of CURL pointers. -static ::firebase::Mutex g_util_curl_mutex; // NOLINT +static ::firebase::Mutex* g_util_curl_mutex = new Mutex(); CURL* g_curl_instance = nullptr; int g_init_ref_count = 0; void* CreateCurlPtr() { // NOLINT - MutexLock lock(g_util_curl_mutex); + MutexLock lock(*g_util_curl_mutex); return curl_easy_init(); } void DestroyCurlPtr(void* curl_ptr) { - MutexLock lock(g_util_curl_mutex); + MutexLock lock(*g_util_curl_mutex); curl_easy_cleanup(static_cast(curl_ptr)); } void Initialize() { - MutexLock curl_lock(g_util_curl_mutex); + MutexLock curl_lock(*g_util_curl_mutex); assert(g_init_ref_count >= 0); if (g_init_ref_count == 0) { g_curl_instance = reinterpret_cast(CreateCurlPtr()); @@ -70,7 +70,7 @@ void Initialize() { } void Terminate() { - MutexLock curl_lock(g_util_curl_mutex); + MutexLock curl_lock(*g_util_curl_mutex); --g_init_ref_count; assert(g_init_ref_count >= 0); if (g_init_ref_count == 0) { diff --git a/app/src/app_common.cc b/app/src/app_common.cc index 3b67e5f9d0..733d3127c5 100644 --- a/app/src/app_common.cc +++ b/app/src/app_common.cc @@ -255,7 +255,7 @@ class LibraryRegistry { }; // Guards g_apps and g_default_app. -static Mutex g_app_mutex; // NOLINT +static Mutex* g_app_mutex = new Mutex(); static std::map>* g_apps; static App* g_default_app = nullptr; LibraryRegistry* LibraryRegistry::library_registry_ = nullptr; @@ -265,7 +265,7 @@ App* AddApp(App* app, std::map* results) { assert(app); App* existing_app = FindAppByName(app->name()); FIREBASE_ASSERT_RETURN(nullptr, !existing_app); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (IsDefaultAppName(app->name())) { assert(!g_default_app); g_default_app = app; @@ -309,7 +309,7 @@ App* AddApp(App* app, std::map* results) { App* FindAppByName(const char* name) { assert(name); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (g_apps) { auto it = g_apps->find(std::string(name)); if (it == g_apps->end()) return nullptr; @@ -325,7 +325,7 @@ App* GetAnyApp() { return g_default_app; } - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (g_apps && !g_apps->empty()) { return g_apps->begin()->second->app; } @@ -334,7 +334,7 @@ App* GetAnyApp() { void RemoveApp(App* app) { assert(app); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (g_apps) { auto it = g_apps->find(std::string(app->name())); bool last_app = false; @@ -369,7 +369,7 @@ void RemoveApp(App* app) { void DestroyAllApps() { std::vector apps_to_delete; App* const default_app = GetDefaultApp(); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (g_apps) { for (auto it = g_apps->begin(); it != g_apps->end(); ++it) { if (it->second->app != default_app) @@ -391,7 +391,7 @@ bool IsDefaultAppName(const char* name) { } void RegisterLibrary(const char* library, const char* version) { - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); LibraryRegistry* registry = LibraryRegistry::Initialize(); if (registry->RegisterLibrary(library, version)) { registry->UpdateUserAgent(); @@ -399,7 +399,7 @@ void RegisterLibrary(const char* library, const char* version) { } void RegisterLibrariesFromUserAgent(const char* user_agent) { - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); LibraryRegistry* registry = LibraryRegistry::Initialize(); // Copy the string into a vector so that we can safely mutate the string. std::vector user_agent_vector(user_agent, @@ -427,12 +427,12 @@ void RegisterLibrariesFromUserAgent(const char* user_agent) { } const char* GetUserAgent() { - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); return LibraryRegistry::Initialize()->GetUserAgent(); } std::string GetLibraryVersion(const char* library) { - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); return LibraryRegistry::Initialize()->GetLibraryVersion(library); } @@ -442,7 +442,7 @@ void GetOuterMostSdkAndVersion(std::string* sdk, std::string* version) { sdk->clear(); version->clear(); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); // Set of library versions to query in order of outer wrapper to inner. // We're only retrieving the outer-most SDK version here as we can only send // one component as part of the user agent string to the storage backend. @@ -466,7 +466,7 @@ void GetOuterMostSdkAndVersion(std::string* sdk, std::string* version) { // Find a logger associated with an app by app name. Logger* FindAppLoggerByName(const char* name) { assert(name); - MutexLock lock(g_app_mutex); + MutexLock lock(*g_app_mutex); if (g_apps) { auto it = g_apps->find(std::string(name)); if (it == g_apps->end()) return nullptr; diff --git a/app/src/callback.cc b/app/src/callback.cc index 6e5590ea23..2ee0a033c9 100644 --- a/app/src/callback.cc +++ b/app/src/callback.cc @@ -187,13 +187,13 @@ class CallbackDispatcher { static CallbackDispatcher* g_callback_dispatcher = nullptr; // Mutex that controls access to g_callback_dispatcher and g_callback_ref_count. -static Mutex g_callback_mutex; // NOLINT +static Mutex* g_callback_mutex = new Mutex(); static int g_callback_ref_count = 0; static Thread::Id g_callback_thread_id; static bool g_callback_thread_id_initialized = false; void Initialize() { - MutexLock lock(g_callback_mutex); + MutexLock lock(*g_callback_mutex); if (g_callback_ref_count == 0) { g_callback_dispatcher = new CallbackDispatcher(); } @@ -202,7 +202,7 @@ void Initialize() { // Add a reference to the module if it's already initialized. static bool InitializeIfInitialized() { - MutexLock lock(g_callback_mutex); + MutexLock lock(*g_callback_mutex); if (IsInitialized()) { Initialize(); return true; @@ -217,7 +217,7 @@ bool IsInitialized() { return g_callback_ref_count > 0; } static void Terminate(int number_of_references_to_remove) { CallbackDispatcher* dispatcher_to_destroy = nullptr; { - MutexLock lock(g_callback_mutex); + MutexLock lock(*g_callback_mutex); if (!g_callback_ref_count) { LogWarning("Callback module already shut down"); return; @@ -239,7 +239,7 @@ static void Terminate(int number_of_references_to_remove) { } void Terminate(bool flush_all) { - MutexLock lock(g_callback_mutex); + MutexLock lock(*g_callback_mutex); int ref_count = 1; // g_callback_ref_count is used to track the number of current references to // g_callback_dispatcher so we need to decrement the reference count by just @@ -253,7 +253,7 @@ void Terminate(bool flush_all) { } void* AddCallback(Callback* callback) { - MutexLock lock(g_callback_mutex); + MutexLock lock(*g_callback_mutex); Initialize(); return g_callback_dispatcher->AddCallback(callback); } diff --git a/app/src/cleanup_notifier.cc b/app/src/cleanup_notifier.cc index 1286519a5e..84d13542ea 100644 --- a/app/src/cleanup_notifier.cc +++ b/app/src/cleanup_notifier.cc @@ -26,12 +26,12 @@ namespace FIREBASE_NAMESPACE { -Mutex CleanupNotifier::cleanup_notifiers_by_owner_mutex_; // NOLINT +Mutex* CleanupNotifier::cleanup_notifiers_by_owner_mutex_ = new Mutex(); std::map *CleanupNotifier::cleanup_notifiers_by_owner_; CleanupNotifier::CleanupNotifier() : cleaned_up_(false) { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); if (!cleanup_notifiers_by_owner_) { cleanup_notifiers_by_owner_ = new std::map(); } @@ -41,7 +41,7 @@ CleanupNotifier::~CleanupNotifier() { CleanupAll(); UnregisterAllOwners(); { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); if (cleanup_notifiers_by_owner_ && cleanup_notifiers_by_owner_->empty()) { delete cleanup_notifiers_by_owner_; cleanup_notifiers_by_owner_ = nullptr; @@ -78,14 +78,14 @@ void CleanupNotifier::CleanupAll() { } void CleanupNotifier::UnregisterAllOwners() { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); while (owners_.begin() != owners_.end()) { UnregisterOwner(this, owners_[0]); } } void CleanupNotifier::RegisterOwner(CleanupNotifier *notifier, void *owner) { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); assert(cleanup_notifiers_by_owner_); auto it = cleanup_notifiers_by_owner_->find(owner); if (it != cleanup_notifiers_by_owner_->end()) UnregisterOwner(it); @@ -94,7 +94,7 @@ void CleanupNotifier::RegisterOwner(CleanupNotifier *notifier, void *owner) { } void CleanupNotifier::UnregisterOwner(CleanupNotifier *notifier, void *owner) { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); assert(cleanup_notifiers_by_owner_); auto it = cleanup_notifiers_by_owner_->find(owner); if (it != cleanup_notifiers_by_owner_->end()) UnregisterOwner(it); @@ -102,7 +102,7 @@ void CleanupNotifier::UnregisterOwner(CleanupNotifier *notifier, void *owner) { void CleanupNotifier::UnregisterOwner( std::map::iterator it) { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); assert(cleanup_notifiers_by_owner_); void *owner = it->first; CleanupNotifier *notifier = it->second; @@ -114,7 +114,7 @@ void CleanupNotifier::UnregisterOwner( } CleanupNotifier *CleanupNotifier::FindByOwner(void *owner) { - MutexLock lock(cleanup_notifiers_by_owner_mutex_); + MutexLock lock(*cleanup_notifiers_by_owner_mutex_); if (!cleanup_notifiers_by_owner_) return nullptr; auto it = cleanup_notifiers_by_owner_->find(owner); return it != cleanup_notifiers_by_owner_->end() ? it->second : nullptr; diff --git a/app/src/cleanup_notifier.h b/app/src/cleanup_notifier.h index 6a0d4d4735..54618fa688 100644 --- a/app/src/cleanup_notifier.h +++ b/app/src/cleanup_notifier.h @@ -102,7 +102,7 @@ class CleanupNotifier { std::vector owners_; // Guards owners_ and cleanup_notifiers_by_owner_. - static Mutex cleanup_notifiers_by_owner_mutex_; + static Mutex* cleanup_notifiers_by_owner_mutex_; // Global map of cleanup notifiers bucketed by owner object. static std::map *cleanup_notifiers_by_owner_; }; diff --git a/app/src/secure/user_secure_manager.cc b/app/src/secure/user_secure_manager.cc index 7e1a8c9b8a..5824ee9a2b 100644 --- a/app/src/secure/user_secure_manager.cc +++ b/app/src/secure/user_secure_manager.cc @@ -50,7 +50,7 @@ namespace secure { using callback::NewCallback; -Mutex UserSecureManager::s_scheduler_mutex_; // NOLINT +Mutex* UserSecureManager::s_scheduler_mutex_ = new Mutex(); scheduler::Scheduler* UserSecureManager::s_scheduler_; int32_t UserSecureManager::s_scheduler_ref_count_; @@ -184,7 +184,7 @@ Future UserSecureManager::DeleteAllData() { } void UserSecureManager::CreateScheduler() { - MutexLock lock(s_scheduler_mutex_); + MutexLock lock(*s_scheduler_mutex_); if (s_scheduler_ == nullptr) { s_scheduler_ = new scheduler::Scheduler(); // reset count @@ -194,7 +194,7 @@ void UserSecureManager::CreateScheduler() { } void UserSecureManager::DestroyScheduler() { - MutexLock lock(s_scheduler_mutex_); + MutexLock lock(*s_scheduler_mutex_); if (s_scheduler_ == nullptr) { // reset count s_scheduler_ref_count_ = 0; diff --git a/app/src/secure/user_secure_manager.h b/app/src/secure/user_secure_manager.h index b548b2f8a3..74d7737b9b 100644 --- a/app/src/secure/user_secure_manager.h +++ b/app/src/secure/user_secure_manager.h @@ -74,7 +74,7 @@ class UserSecureManager { static void DestroyScheduler(); // Guards static scheduler pointer - static Mutex s_scheduler_mutex_; // NOLINT + static Mutex* s_scheduler_mutex_; static scheduler::Scheduler* s_scheduler_; static int32_t s_scheduler_ref_count_; diff --git a/app/src/util.cc b/app/src/util.cc index 03b51f668e..97cee7ad18 100644 --- a/app/src/util.cc +++ b/app/src/util.cc @@ -158,12 +158,12 @@ Future ModuleInitializer::InitializeLastResult() { } std::map* AppCallback::callbacks_; -Mutex AppCallback::callbacks_mutex_; // NOLINT +Mutex* AppCallback::callbacks_mutex_ = new Mutex(); void AppCallback::NotifyAllAppCreated( firebase::App* app, std::map* results) { if (results) results->clear(); - MutexLock lock(callbacks_mutex_); + MutexLock lock(*callbacks_mutex_); if (!callbacks_) return; for (std::map::const_iterator it = callbacks_->begin(); @@ -177,7 +177,7 @@ void AppCallback::NotifyAllAppCreated( } void AppCallback::NotifyAllAppDestroyed(firebase::App* app) { - MutexLock lock(callbacks_mutex_); + MutexLock lock(*callbacks_mutex_); if (!callbacks_) return; for (std::map::const_iterator it = callbacks_->begin(); @@ -189,7 +189,7 @@ void AppCallback::NotifyAllAppDestroyed(firebase::App* app) { // Determine whether a module callback is enabled, by name. bool AppCallback::GetEnabledByName(const char* name) { - MutexLock lock(callbacks_mutex_); + MutexLock lock(*callbacks_mutex_); if (!callbacks_) return false; std::map::const_iterator it = callbacks_->find(std::string(name)); @@ -201,7 +201,7 @@ bool AppCallback::GetEnabledByName(const char* name) { // Enable a module callback by name. void AppCallback::SetEnabledByName(const char* name, bool enable) { - MutexLock lock(callbacks_mutex_); + MutexLock lock(*callbacks_mutex_); if (!callbacks_) return; std::map::const_iterator it = callbacks_->find(std::string(name)); @@ -214,7 +214,7 @@ void AppCallback::SetEnabledByName(const char* name, bool enable) { } void AppCallback::SetEnabledAll(bool enable) { - MutexLock lock(callbacks_mutex_); + MutexLock lock(*callbacks_mutex_); if (!callbacks_) return; LogDebug("%s all app initializers", enable ? "Enabling" : "Disabling"); for (std::map::const_iterator it = @@ -242,13 +242,13 @@ void AppCallback::AddCallback(AppCallback* callback) { } } -Mutex StaticFutureData::s_futures_mutex_; // NOLINT +Mutex* StaticFutureData::s_futures_mutex_ = new Mutex(); std::map* StaticFutureData::s_future_datas_; // static void StaticFutureData::CleanupFutureDataForModule( const void* module_identifier) { - MutexLock lock(s_futures_mutex_); + MutexLock lock(*s_futures_mutex_); if (s_future_datas_ == nullptr) return; @@ -264,7 +264,7 @@ void StaticFutureData::CleanupFutureDataForModule( // static StaticFutureData* StaticFutureData::GetFutureDataForModule( const void* module_identifier, int num_functions) { - MutexLock lock(s_futures_mutex_); + MutexLock lock(*s_futures_mutex_); if (s_future_datas_ == nullptr) { s_future_datas_ = new std::map; diff --git a/app/src/util.h b/app/src/util.h index cfc59ca788..452ca9019d 100644 --- a/app/src/util.h +++ b/app/src/util.h @@ -168,7 +168,7 @@ class AppCallback { // Mutex which controls access to callbacks during notification. // NOTE: This violates Google's C++ style guide as we do not have access to // module initializers in //base so this replicates part of that behavior. - static Mutex callbacks_mutex_; + static Mutex* callbacks_mutex_; }; // Register app callbacks for a module. @@ -225,7 +225,7 @@ class StaticFutureData { ReferenceCountedFutureImpl api_; // Mutex which controls access to s_future_datas_ - static Mutex s_futures_mutex_; + static Mutex* s_futures_mutex_; // Static storage of StaticFutureDatas per module. Keyed on a void pointer // that should be declared in the module to serve as a unique identifier. diff --git a/auth/src/auth.cc b/auth/src/auth.cc index 64f39855c4..52abd08ff3 100644 --- a/auth/src/auth.cc +++ b/auth/src/auth.cc @@ -63,11 +63,11 @@ DEFINE_FIREBASE_VERSION_STRING(FirebaseAuth); // reference. // TODO(jsanmiya): Ensure all the Auths are destroyed on shutdown. std::map g_auths; -Mutex g_auths_mutex; // NOLINT +Mutex* g_auths_mutex = new Mutex(); // static Auth* Auth::GetAuth(App* app, InitResult* init_result_out) { - MutexLock lock(g_auths_mutex); + MutexLock lock(*g_auths_mutex); // Return the Auth if it already exists. Auth* existing_auth = FindAuth(app); if (existing_auth) { @@ -95,7 +95,7 @@ Auth* Auth::GetAuth(App* app, InitResult* init_result_out) { // static Auth* Auth::FindAuth(App* app) { - MutexLock lock(g_auths_mutex); + MutexLock lock(*g_auths_mutex); // Return the Auth if it already exists. std::map::iterator it = g_auths.find(app); if (it != g_auths.end()) { @@ -137,7 +137,7 @@ Auth::Auth(App* app, void* auth_impl) : auth_data_(new AuthData) { } void Auth::DeleteInternal() { - MutexLock lock(g_auths_mutex); + MutexLock lock(*g_auths_mutex); if (!auth_data_) return; diff --git a/auth/src/desktop/rpcs/auth_request.cc b/auth/src/desktop/rpcs/auth_request.cc index c927e53bfd..18dc880481 100644 --- a/auth/src/desktop/rpcs/auth_request.cc +++ b/auth/src/desktop/rpcs/auth_request.cc @@ -33,8 +33,8 @@ AuthRequest::AuthRequest(const char* schema) : RequestJson(schema) { // dependencies. static std::string auth_user_agent; // NOLINT static std::string extended_auth_user_agent; // NOLINT - static Mutex user_agent_mutex; // NOLINT - MutexLock lock(user_agent_mutex); + static Mutex* user_agent_mutex = new Mutex(); + MutexLock lock(*user_agent_mutex); if (auth_user_agent.empty()) { std::string sdk; std::string version; diff --git a/firestore/src/common/firestore.cc b/firestore/src/common/firestore.cc index 635801f599..0b3e309755 100644 --- a/firestore/src/common/firestore.cc +++ b/firestore/src/common/firestore.cc @@ -47,7 +47,7 @@ const char* GetPlatform() { #endif } -Mutex g_firestores_lock; // NOLINT +Mutex* g_firestores_lock = new Mutex(); std::map* g_firestores = nullptr; // Ensures that the cache is initialized. @@ -88,7 +88,7 @@ Firestore* Firestore::GetInstance(App* app, InitResult* init_result_out) { FIREBASE_ASSERT_MESSAGE(app != nullptr, "Provided firebase::App must not be null."); - MutexLock lock(g_firestores_lock); + MutexLock lock(*g_firestores_lock); Firestore* from_cache = FindFirestoreInCache(app, init_result_out); if (from_cache) { @@ -111,7 +111,7 @@ Firestore* Firestore::CreateFirestore(App* app, FirestoreInternal* internal, FIREBASE_ASSERT_MESSAGE(internal != nullptr, "Provided FirestoreInternal must not be null."); - MutexLock lock(g_firestores_lock); + MutexLock lock(*g_firestores_lock); Firestore* from_cache = FindFirestoreInCache(app, init_result_out); FIREBASE_ASSERT_MESSAGE(from_cache == nullptr, @@ -168,7 +168,7 @@ Firestore::Firestore(FirestoreInternal* internal) Firestore::~Firestore() { DeleteInternal(); } void Firestore::DeleteInternal() { - MutexLock lock(g_firestores_lock); + MutexLock lock(*g_firestores_lock); if (!internal_) return; From 222312232ce2495df980f415eec800b0c77cba78 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 22 Mar 2021 15:55:06 -0400 Subject: [PATCH 2/2] Add change log entry --- release_build_files/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release_build_files/readme.md b/release_build_files/readme.md index a9be9d8166..c0f630faa2 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -579,6 +579,9 @@ code. - Database: Fixed a potential crash that can occur as a result of a race condidtion when adding, removing and deleting `ValueListener`s or `ChildListener`s rapidly. + - General: Fixed rare crashes at application exit when destructors were + being executed + ([#345](https://github.com/firebase/firebase-cpp-sdk/pull/345)). ### 7.1.1 - Changes