summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2019-01-27 19:24:55 -0300
committerJuan Linietsky <reduzio@gmail.com>2019-01-27 19:24:55 -0300
commit11642b92d1b72154268edcde76b0b53d309e22eb (patch)
tree3053935b81c0cdddfe77c62a869c1b3678bd7d09 /core/io
parent0d438c7b18521833be23af37e0d9c1c7a66b669c (diff)
Avoid cyclic resource loading of any type, fixes #22673
Diffstat (limited to 'core/io')
-rw-r--r--core/io/resource_loader.cpp126
-rw-r--r--core/io/resource_loader.h12
2 files changed, 128 insertions, 10 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index b8bc24c1de..02e4d8dc7a 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -53,6 +53,12 @@ Error ResourceInteractiveLoader::wait() {
return err;
}
+ResourceInteractiveLoader::~ResourceInteractiveLoader() {
+ if (path_loading != String()) {
+ ResourceLoader::_remove_from_loading_map(path_loading);
+ }
+}
+
bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_for_type) const {
String extension = p_path.get_extension();
@@ -280,6 +286,39 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
return RES();
}
+bool ResourceLoader::_add_to_loading_map(const String &p_path) {
+
+ bool success;
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ if (loading_map.has(p_path)) {
+ success = false;
+ } else {
+ loading_map[p_path] = true;
+ success = true;
+ }
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+
+ return success;
+}
+
+void ResourceLoader::_remove_from_loading_map(const String &p_path) {
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ loading_map.erase(p_path);
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+}
+
RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {
if (r_error)
@@ -292,6 +331,15 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
if (!p_no_cache) {
+
+ {
+ bool success = _add_to_loading_map(local_path);
+ if (!success) {
+ ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
+ ERR_FAIL_V(RES());
+ }
+ }
+
//lock first if possible
if (ResourceCache::lock) {
ResourceCache::lock->read_lock();
@@ -310,7 +358,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
if (ResourceCache::lock) {
ResourceCache::lock->read_unlock();
}
- print_verbose("Loading resource: " + local_path + " (cached)");
+ _remove_from_loading_map(local_path);
return res;
}
}
@@ -322,12 +370,21 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
bool xl_remapped = false;
String path = _path_remap(local_path, &xl_remapped);
- ERR_FAIL_COND_V(path == "", RES());
+ if (path == "") {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+ ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
+ ERR_FAIL_V(RES());
+ }
print_verbose("Loading resource: " + path);
RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error);
if (res.is_null()) {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
return RES();
}
if (!p_no_cache)
@@ -346,6 +403,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
}
#endif
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+
if (_loaded_callback) {
_loaded_callback(res, p_path);
}
@@ -394,19 +455,36 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
else
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- if (!p_no_cache && ResourceCache::has(local_path)) {
+ if (!p_no_cache) {
- print_verbose("Loading resource: " + local_path + " (cached)");
- Ref<Resource> res_cached = ResourceCache::get(local_path);
- Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault));
+ bool success = _add_to_loading_map(local_path);
+ if (!success) {
+ ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
+ ERR_FAIL_V(RES());
+ }
- ril->resource = res_cached;
- return ril;
+ if (ResourceCache::has(local_path)) {
+
+ print_verbose("Loading resource: " + local_path + " (cached)");
+ Ref<Resource> res_cached = ResourceCache::get(local_path);
+ Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault));
+
+ ril->resource = res_cached;
+ ril->path_loading = local_path;
+ return ril;
+ }
}
bool xl_remapped = false;
String path = _path_remap(local_path, &xl_remapped);
- ERR_FAIL_COND_V(path == "", Ref<ResourceInteractiveLoader>());
+ if (path == "") {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+ ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
+ ERR_FAIL_V(RES());
+ }
+
print_verbose("Loading resource: " + path);
bool found = false;
@@ -418,14 +496,21 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
Ref<ResourceInteractiveLoader> ril = loader[i]->load_interactive(path, local_path, r_error);
if (ril.is_null())
continue;
- if (!p_no_cache)
+ if (!p_no_cache) {
ril->set_local_path(local_path);
+ ril->path_loading = local_path;
+ }
+
if (xl_remapped)
ril->set_translation_remapped(true);
return ril;
}
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+
if (found) {
ERR_EXPLAIN("Failed loading resource: " + path);
} else {
@@ -833,6 +918,27 @@ void ResourceLoader::remove_custom_loaders() {
}
}
+Mutex *ResourceLoader::loading_map_mutex = NULL;
+HashMap<String, int> ResourceLoader::loading_map;
+
+void ResourceLoader::initialize() {
+#ifndef NO_THREADS
+ loading_map_mutex = Mutex::create();
+#endif
+}
+
+void ResourceLoader::finalize() {
+#ifndef NO_THREADS
+ const String *K = NULL;
+ while ((K = loading_map.next(K))) {
+ ERR_PRINTS("Exited while resource is being loaded: " + *K);
+ }
+ loading_map.clear();
+ memdelete(loading_map_mutex);
+ loading_map_mutex = NULL;
+#endif
+}
+
ResourceLoadErrorNotify ResourceLoader::err_notify = NULL;
void *ResourceLoader::err_notify_ud = NULL;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 6c0e9d5554..622b74a998 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -40,6 +40,8 @@
class ResourceInteractiveLoader : public Reference {
GDCLASS(ResourceInteractiveLoader, Reference);
+ friend class ResourceLoader;
+ String path_loading;
protected:
static void _bind_methods();
@@ -54,6 +56,7 @@ public:
virtual Error wait();
ResourceInteractiveLoader() {}
+ ~ResourceInteractiveLoader();
};
class ResourceFormatLoader : public Reference {
@@ -110,12 +113,18 @@ class ResourceLoader {
static SelfList<Resource>::List remapped_list;
friend class ResourceFormatImporter;
+ friend class ResourceInteractiveLoader;
//internal load function
static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error);
static ResourceLoadedCallback _loaded_callback;
static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path);
+ static Mutex *loading_map_mutex;
+ static HashMap<String, int> loading_map;
+
+ static bool _add_to_loading_map(const String &p_path);
+ static void _remove_from_loading_map(const String &p_path);
public:
static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL);
@@ -170,6 +179,9 @@ public:
static void remove_custom_resource_format_loader(String script_path);
static void add_custom_loaders();
static void remove_custom_loaders();
+
+ static void initialize();
+ static void finalize();
};
#endif