Skip to content

Commit

Permalink
godotengine#66231 enable detection of gdextension library change for …
Browse files Browse the repository at this point in the history
…editor restart and experimental work on dynamic reload
  • Loading branch information
jpedrick committed May 5, 2023
1 parent 615d618 commit 329fff6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 10 deletions.
22 changes: 22 additions & 0 deletions core/extension/gdextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,11 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library

StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);

ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
#endif

Extension *parent_extension = nullptr;

Expand Down Expand Up @@ -381,6 +384,7 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
Extension *ext = &self->extension_classes[class_name];
ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");

OS::get_singleton()->print("Unregistering extension class %s\n",String(class_name).utf8().get_data());
ClassDB::unregister_extension_class(class_name);
if (ext->gdextension.parent != nullptr) {
ext->gdextension.parent->children.erase(&ext->gdextension);
Expand All @@ -394,6 +398,21 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte
*(String *)r_path = self->library_path;
}

Error GDExtension::reload(){
auto old_library = library;

library = nullptr;

auto status = open_library(library_path, entry_symbol);
OS::get_singleton()->print("Opened new, closing old: %s %p\n",String(library_path).utf8().get_data(), old_library);
if( old_library ){
auto close_status = OS::get_singleton()->close_dynamic_library(old_library);
ERR_FAIL_COND_V_MSG(close_status != Error::OK, close_status, "Can't close dynamic library: " + library_path + ", error:.");
}

return status;
}

Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path);
if (err != OK) {
Expand All @@ -415,6 +434,9 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb

if (initialization_function(&gdextension_interface, this, &initialization)) {
level_initialized = -1;
modified_time = FileAccess::get_modified_time(library_path);
entry_symbol = p_entry_symbol;

return OK;
} else {
ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
Expand Down
2 changes: 2 additions & 0 deletions core/extension/gdextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class GDExtension : public Resource {

void *library = nullptr; // pointer if valid,
String library_path;
String entry_symbol;

struct Extension {
ObjectGDExtension gdextension;
Expand Down Expand Up @@ -75,6 +76,7 @@ class GDExtension : public Resource {

Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
Error reload();

enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
Expand Down
53 changes: 46 additions & 7 deletions core/extension/gdextension_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,53 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &
}

GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String &p_path) {
if ( gdextension_map.has(p_path) ){
return GDExtensionManager::LOAD_STATUS_NEEDS_RESTART;
//unload_extension(p_path);
//return load_extension(p_path);
}
else{
return LOAD_STATUS_NOT_LOADED;
#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD
return LOAD_STATUS_NEEDS_RESTART;
#else
auto extension_it = gdextension_map.find(p_path);
if (extension_it != gdextension_map.end()){
Ref<GDExtension> extension = extension_it->value;

auto reload_status = extension->reload();
if (reload_status != Error::OK){
return LOAD_STATUS_NEEDS_RESTART;
}

if (level >= 0) { // Already initialized up to some level.
int32_t minimum_level = extension->get_minimum_library_initialization_level();
if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
return LOAD_STATUS_NEEDS_RESTART;
}
// Deinitialize down to current level.
for (int32_t i = level; i >= minimum_level; i--) {
extension->deinitialize_library(GDExtension::InitializationLevel(i));
}
}

for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
gdextension_class_icon_paths.erase(kv.key);
}

if (level >= 0) { // Already initialized up to some level.
int32_t minimum_level = extension->get_minimum_library_initialization_level();
if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
return LOAD_STATUS_NEEDS_RESTART;
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
extension->initialize_library(GDExtension::InitializationLevel(i));
}
}

for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
gdextension_class_icon_paths[kv.key] = kv.value;
}

return LOAD_STATUS_OK;
}

return LOAD_STATUS_NOT_LOADED;
#endif
}

GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) {
Expand Down
8 changes: 8 additions & 0 deletions core/object/class_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "core/object/script_language.h"
#include "core/os/mutex.h"
#include "core/version.h"
#include "core/os/os.h"

#define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock);
#define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock);
Expand Down Expand Up @@ -1538,9 +1539,16 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
void ClassDB::register_extension_class(ObjectGDExtension *p_extension) {
GLOBAL_LOCK_FUNCTION;

#ifndef SUPPORT_DYNAMIC_GDEXTENSION_RELOAD
ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name));
#else
if (classes.has(p_extension->class_name)){
unregister_extension_class(p_extension->class_name);
}
#endif
ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name));


ClassInfo *parent = classes.getptr(p_extension->parent_class_name);

ClassInfo c;
Expand Down
11 changes: 8 additions & 3 deletions editor/editor_file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2518,13 +2518,18 @@ bool EditorFileSystem::_scan_extensions() {
}

for (const auto& E : extensions_changed){
needs_restart = true;
OS::get_singleton()->print("Extension %s changed, needs reload.\n", E.utf8().get_data());
GDExtensionManager::LoadStatus st = em->reload_extension(E);
if ( st == GDExtensionManager::LOAD_STATUS_FAILED ){
EditorNode::get_singleton()->add_io_error("Error removing extension: " + E);
if (st == GDExtensionManager::LOAD_STATUS_FAILED){
ERR_PRINT("Error loading GDExtension: " + E);
EditorNode::get_singleton()->add_io_error("Error reloading extension: " + E);
} else if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
WARN_PRINT("GDExtension changed, need restart: " + E);
needs_restart = true;
} else if(st == GDExtensionManager::LOAD_STATUS_OK) {
OS::get_singleton()->print("Extension %s reload OK.\n",E.utf8().get_data());
}

}

return needs_restart;
Expand Down

0 comments on commit 329fff6

Please sign in to comment.