Правильная загрузка, выгрузка модуля GTypeModule

Имеется некая система плагинов, реализованная при помощи системы модулей GTypeModule.

При попытке реализации загрузки типов из плагинов, была проблема, в том что они не были зарегистрированы, и все проблемы были решены путем объявления всех загружаемых типов, как динамические G_DEFINE_DYNAMIC_TYPE_EXTENDED(...), исключением является модуль, который возвращается функцией инициализации плагина.

Вроде бы все хорошо, типы зарегистрированы, g_object_new(...) успешно создает объекты, вызывает их финализаторы, конструкторы, и т.д.

Проблема: При уничтожении объектов, ссылки на модуль не уменьшается.

Представим что интерфейсы такие

plugin.h:

#ifndef RIN_PLUGIN_H
#define RIN_PLUGIN_H 1

#include <glib-object.h>
#include <gmodule.h>

G_BEGIN_DECLS

typedef struct _RinKit RinKit;
typedef struct _RinProbe RinProbe;

#define RIN_PLUGIN_ENTRY        "rin_plugin_entry"
#define RIN_PLUGIN_CREATE_KIT   "rin_plugin_create_kit"
#define RIN_PLUGIN_CREATE_PROBE "rin_plugin_create_probe"

typedef GTypeModule *(*RinPluginEntry)        (void);
typedef RinKit      *(*RinPluginCreateKit)    (void);
typedef RinProbe    *(*RinPluginCreateProbe)  (void);

#define RIN_API G_MODULE_EXPORT

typedef struct _RinProject RinProject;

#define RIN_TYPE_KIT (rin_kit_get_type())
#define RIN_TYPE_PROBE (rin_probe_get_type())
#define RIN_TYPE_PLUGIN (rin_plugin_get_type())

RIN_API
G_DECLARE_INTERFACE(RinKit, rin_kit, RIN, KIT, GObject)

RIN_API
G_DECLARE_INTERFACE(RinProbe, rin_probe, RIN, PROBE, GObject)

RIN_API
G_DECLARE_DERIVABLE_TYPE(RinPlugin, rin_plugin, RIN, PLUGIN, GTypeModule)

struct _RinPluginClass {
  GTypeModuleClass parent_class;

  /* reserved for future expansion and ABI compatability */
  gpointer reserved1;
  gpointer reserved2;
  gpointer reserved3;
  gpointer reserved4;
};

struct _RinProbeInterface {
  GTypeInterface parent_iface;

  /*< public >*/
  gboolean
  (*path)
  (RinProbe *self, gchar const *program_path);

  gboolean
  (*project)
  (RinProbe *self, RinProject *project);

  /* reserved for future expansion and ABI compatability */
  gpointer reserved1;
  gpointer reserved2;
  gpointer reserved3;
  gpointer reserved4;
};

struct _RinKitInterface {
  GTypeInterface parent_iface;

  /*< public >*/
  RinProject *
  (*create_project_from_path)
  (RinKit *self, gchar const *path);

  gboolean
  (*save_project)
  (RinKit *self, RinProject *project, gchar const *path);

  gboolean
  (*load_project)
  (RinKit *self, RinProject *project, gchar const *path);

  gboolean
  (*make_patch)
  (RinKit *self, RinProject *project, gchar const *path);

  /* reserved for future expansion and ABI compatability */
  gpointer reserved1;
  gpointer reserved2;
  gpointer reserved3;
  gpointer reserved4;
};

#define RIN_DEFINE_PLUGIN(ModuleObjName, module_obj_name, MODULE, OBJ_NAME, ModuleName)   \
G_MODULE_EXPORT GTypeModule *rin_plugin_entry(void);                                      \
G_DECLARE_FINAL_TYPE(ModuleObjName, module_obj_name, MODULE, OBJ_NAME, RinPlugin)         \
struct _##ModuleObjName {                                                                 \
  struct _RinPlugin parent;                                                               \
};                                                                                        \
G_DEFINE_TYPE(ModuleObjName, module_obj_name, RIN_TYPE_PLUGIN)                            \
static void module_obj_name##_class_init(ModuleObjName##Class *klass) {                   \
  GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS(klass);                            \
  module_class->load = module_obj_name##_load;                                            \
  module_class->unload = module_obj_name##_unload;                                        \
}                                                                                         \
static void module_obj_name##_init(ModuleObjName *self) {                                 \
  (void) self;                                                                            \
}                                                                                         \
G_MODULE_EXPORT GTypeModule *rin_plugin_entry(void) {                                     \
  static GTypeModule *module = NULL;                                                      \
  if (module == NULL) {                                                                   \
    module = g_object_new(module_obj_name##_get_type(), NULL);                            \
    if (module) {                                                                         \
      g_type_module_set_name(module, ModuleName);                                         \
    }                                                                                     \
  }                                                                                       \
  return module;                                                                          \
}                                                                                         \
G_MODULE_EXPORT                                                                           \
RinKit *                                                                                  \
rin_plugin_create_kit(void);                                                              \
G_MODULE_EXPORT                                                                           \
RinProbe *                                                                                \
rin_plugin_create_probe(void);

RIN_API
RinProject *
rin_kit_create_project_from_path(RinKit *self, gchar const *path);

RIN_API
gboolean
rin_kit_save_project(RinKit *self, RinProject *project, gchar const *path);

RIN_API
gboolean
rin_kit_load_project(RinKit *self, RinProject *project, gchar const *path);

RIN_API
gboolean
rin_kit_make_patch(RinKit *self, RinProject *project, gchar const *path);

RIN_API
gboolean
rin_probe_path(RinProbe *self, gchar const *path);

RIN_API
gboolean
rin_probe_project(RinProbe *self, RinProject *project);

G_END_DECLS

#endif /* RIN_PLUGIN_H */

plugin.c:

#include "rin-plugin.h"

G_DEFINE_ABSTRACT_TYPE(RinPlugin, rin_plugin, G_TYPE_TYPE_MODULE)
G_DEFINE_INTERFACE(RinProbe, rin_probe, G_TYPE_OBJECT)
G_DEFINE_INTERFACE(RinKit, rin_kit, G_TYPE_OBJECT)

static
void
rin_plugin_init(RinPlugin *self) { }

static
void
rin_plugin_class_init(RinPluginClass *klass) { }

static
void
rin_probe_default_init(RinProbeInterface *klass) {
  klass->path = NULL;
  klass->project = NULL;

  klass->reserved1
    = klass->reserved2
    = klass->reserved3
    = klass->reserved4
      = NULL;
}

static
void
rin_kit_default_init(RinKitInterface *klass) {
  klass->create_project_from_path = NULL;
  klass->load_project = NULL;
  klass->save_project = NULL;
  klass->make_patch = NULL;

  klass->reserved1
    = klass->reserved2
    = klass->reserved3
    = klass->reserved4
      = NULL;
}

RinProject *
rin_kit_create_project_from_path(RinKit *self, gchar const *path) {
  RinKitInterface *iface;
  g_return_val_if_fail(RIN_IS_KIT(self), NULL);

  iface = RIN_KIT_GET_IFACE(self);

  return
    iface->create_project_from_path
      ? (iface->create_project_from_path)(self, path)
      : NULL;
}

gboolean
rin_kit_save_project(RinKit *self, RinProject *project, gchar const *path) {
  RinKitInterface *iface;
  g_return_val_if_fail(RIN_IS_KIT(self), FALSE);

  iface = RIN_KIT_GET_IFACE(self);

  return
    iface->save_project
      ? (iface->save_project)(self, project, path)
      : FALSE;
}

gboolean
rin_kit_load_project(RinKit *self, RinProject *project, gchar const *path) {
  RinKitInterface *iface;
  g_return_val_if_fail(RIN_IS_KIT(self), FALSE);

  iface = RIN_KIT_GET_IFACE(self);

  return
    iface->load_project
      ? (iface->load_project)(self, project, path)
      : FALSE;
}

gboolean
rin_kit_make_patch(RinKit *self, RinProject *project, gchar const *path) {
  RinKitInterface *iface;
  g_return_val_if_fail(RIN_IS_KIT(self), FALSE);

  iface = RIN_KIT_GET_IFACE(self);

  return
    iface->make_patch
      ? (iface->make_patch)(self, project, path)
      : FALSE;
}

gboolean
rin_probe_path(RinProbe *self, gchar const *path) {
  RinProbeInterface *iface;
  g_return_val_if_fail(RIN_IS_PROBE(self), FALSE);

  iface = RIN_PROBE_GET_IFACE(self);

  return
    iface->path
      ? (iface->path)(self, path)
      : FALSE;
}

gboolean
rin_probe_project(RinProbe *self, RinProject *project) {
  RinProbeInterface *iface;
  g_return_val_if_fail(RIN_IS_PROBE(self), FALSE);

  iface = RIN_PROBE_GET_IFACE(self);

  return
    iface->project
      ? (iface->project)(self, project)
      : FALSE;
}

Регистрирую модуль:

#define G_LOG_DOMAIN "RinPlugin-SoulWorker"
#define RIN_PLUGIN_SOUL_WORKER_NAME ("Rin Plugin SoulWorker")

#include "rin-plugin.h"

#define RIN_NOT_IMPLEMENTED() (g_warning("%s: Not implemented.", __func__))

#define RIN_TYPE_PROBE_SOUL_WORKER (rin_probe_soul_worker_get_type())

G_DECLARE_FINAL_TYPE(RinProbeSoulWorker,
  rin_probe_soul_worker,
  RIN, PROBE_SOUL_WORKER,
  GObject
)

struct _RinProbeSoulWorker {
  GObject parent;
};

static
void
rin_probe_soul_worker_finalize(GObject *object) { }

static
gboolean
rin_probe_soul_worker_path(RinProbe *self, gchar const *path) {
  if (path) {
    g_message("Probe path: %s", path);
    return TRUE;
  } else {
    g_message("Probe path: NULL");
    return FALSE;
  }
}

static
gboolean
rin_probe_soul_worker_project(RinProbe *self, RinProject *project) {
  if (project) {
    g_message("Probe project: %p", project);
    return TRUE;
  } else {
    g_message("Probe project: NULL");
    return FALSE;
  }
}

static
void
rin_probe_soul_worker_interface_init(RinProbeInterface *iface) {
  iface->path     = rin_probe_soul_worker_path;
  iface->project  = rin_probe_soul_worker_project;
}

G_DEFINE_DYNAMIC_TYPE_EXTENDED(RinProbeSoulWorker,
  rin_probe_soul_worker,
  G_TYPE_OBJECT,
  G_TYPE_FLAG_FINAL,
  G_IMPLEMENT_INTERFACE_DYNAMIC(RIN_TYPE_PROBE, rin_probe_soul_worker_interface_init)
)

static
void
rin_probe_soul_worker_class_init(RinProbeSoulWorkerClass *klass) {
  if (G_OBJECT_CLASS(klass)->finalize) {
    G_OBJECT_CLASS(klass)->finalize = rin_probe_soul_worker_finalize;
  }
}

static
void
rin_probe_soul_worker_class_finalize(RinProbeSoulWorkerClass *klass) { }

static
void
rin_probe_soul_worker_init(RinProbeSoulWorker *self) { }

#define RIN_TYPE_KIT_SOUL_WORKER (rin_kit_soul_worker_get_type())

G_DECLARE_FINAL_TYPE(RinKitSoulWorker,
  rin_kit_soul_worker,
  RIN, KIT_SOUL_WORKER,
  GObject)

struct _RinKitSoulWorker {
  GObject parent;
  RinProject *project;
  gchar *path;
  gboolean is_loaded;
};

static
RinProject *
rin_kit_soul_worker_create_project_from_path(RinKit *self, gchar const *path) {
  RIN_NOT_IMPLEMENTED();
  return NULL;
}

static
gboolean
rin_kit_soul_worker_load_project(RinKit *self, RinProject *project, gchar const *path) {
  RIN_NOT_IMPLEMENTED();
  return FALSE;
}

static
gboolean
rin_kit_soul_worker_save_project(RinKit *self, RinProject *project, gchar const *path) {
  RIN_NOT_IMPLEMENTED();
  return FALSE;
}

static
gboolean
rin_kit_soul_worker_make_patch(RinKit *self, RinProject *project, gchar const *path) {
  RIN_NOT_IMPLEMENTED();
  return FALSE;
}

static
void
rin_kit_soul_worker_interface_init(RinKitInterface *iface) {
  iface->create_project_from_path = rin_kit_soul_worker_create_project_from_path;
  iface->load_project = rin_kit_soul_worker_load_project;
  iface->save_project = rin_kit_soul_worker_save_project;
  iface->make_patch = rin_kit_soul_worker_make_patch;
}

G_DEFINE_DYNAMIC_TYPE_EXTENDED(RinKitSoulWorker,
  rin_kit_soul_worker,
  G_TYPE_OBJECT,
  G_TYPE_FLAG_FINAL,
  G_IMPLEMENT_INTERFACE_DYNAMIC(RIN_TYPE_KIT, rin_kit_soul_worker_interface_init)
)

static
void
rin_kit_soul_worker_init(RinKitSoulWorker *self) {
  (void) self;
}

static
void
rin_kit_soul_worker_finalize(GObject *object) {
  if (G_OBJECT_CLASS(rin_kit_soul_worker_parent_class)->finalize) {
    G_OBJECT_CLASS(rin_kit_soul_worker_parent_class)->finalize(object);
  }
}

static
void
rin_kit_soul_worker_class_init(RinKitSoulWorkerClass *klass) {
  (void) klass;
  klass->parent_class.finalize = rin_kit_soul_worker_finalize;
}

static
void
rin_kit_soul_worker_class_finalize(RinKitSoulWorkerClass *klass) {
  (void) klass;
}

#define RIN_TYPE_PLUGIN_SOUL_WORKER (rin_plugin_soul_worker_get_type())

static
gboolean
rin_plugin_soul_worker_load(GTypeModule *module) {
  g_warning("Plugin loaded.");
  rin_kit_soul_worker_register_type(module);
  rin_probe_soul_worker_register_type(module);
  return TRUE;
}

static
void
rin_plugin_soul_worker_unload(GTypeModule *module) {
  g_warning("Plugin unloaded.");
  (void) module;
}

RIN_DEFINE_PLUGIN(RinPluginSoulWorker,
  rin_plugin_soul_worker,
  RIN, PLUGIN_SOUL_WORKER,
  "RinPlugin SoulWorker")

G_MODULE_EXPORT
RinKit *
rin_plugin_create_kit(void) {
  return
    RIN_KIT(g_object_new(RIN_TYPE_KIT_SOUL_WORKER, NULL));
}

G_MODULE_EXPORT
RinProbe *
rin_plugin_create_probe(void) {
  return
    RIN_PROBE(g_object_new(RIN_TYPE_PROBE_SOUL_WORKER, NULL));
}

Тестовое приложение:

#include <gmodule.h>

#include "rin-plugin.h"

#define RIN_MODULE_PATH "plugins/librin-plugin-soul-worker.so"

int
main(int argc, char *argv[]) {
  GModule *module;
  GTypeModule *plugin;

  RinPluginEntry plugin_entry;
  RinPluginCreateKit plugin_create_kit;
  RinPluginCreateProbe plugin_create_probe;

  RinKit *plugin_kit_object;

  module = g_module_open(RIN_MODULE_PATH, G_MODULE_BIND_LAZY);

  g_module_symbol(module, RIN_PLUGIN_ENTRY, (gpointer *) &plugin_entry);
  g_module_symbol(module, RIN_PLUGIN_CREATE_KIT, (gpointer *) &plugin_create_kit);
  g_module_symbol(module, RIN_PLUGIN_CREATE_PROBE, (gpointer *) &plugin_create_probe);

  plugin = plugin_entry();

  g_type_module_use(plugin);

  plugin_kit_object = plugin_create_kit();

  rin_kit_save_project(plugin_kit_object, NULL, NULL);
  rin_kit_load_project(plugin_kit_object, NULL, NULL);
  rin_kit_create_project_from_path(plugin_kit_object, NULL);
  rin_kit_make_patch(plugin_kit_object, NULL, NULL);

  g_object_unref(plugin_kit_object);
  g_object_unref(plugin);

  g_type_module_unuse(plugin);

  g_module_close(module);

  return 0;
}

Ссылки до вызова g_type_module_unuse(...):

До вызова

Ссылки после вызова g_type_module_unuse(...):

После вызова

Что нужно сделать чтобы после уничтожения объектов из плагина уничтожался и модуль который их загрузил?

P.S. Что бы долго не искать функции загрузки выгрузки:

static
gboolean
rin_plugin_soul_worker_load(GTypeModule *module) {
  g_warning("Plugin loaded.");
  rin_kit_soul_worker_register_type(module);
  rin_probe_soul_worker_register_type(module);
  return TRUE;
}

static
void
rin_plugin_soul_worker_unload(GTypeModule *module) {
  g_warning("Plugin unloaded.");
  (void) module;
}

Ответы (1 шт):

Автор решения: evo

В общем и сложном:

Не глядя на нововведения библиотеки glib, я надеялся на одинаковую работу, т.к. не было выхода мажорной версии, версия glib 2.84, прекращает поддержку учёта ссылок на тип GTypeClass, и функция g_type_class_unref, становится пустышкой (заглушкой), далее говорится в обновлении о том что полноценная выгрузка типов становится не доступна, и ранее зарегистрированные типы, остаются в системе типов до конца работы приложения.

Deprecated since: 2.84.

Type class reference counting has been removed and type classes now cannot be finalized. This function no longer does anything.

Собственно говоря, эта функция которая вызывалась для финализации класса зарегистрированного типа.

Теперь выгрузка становится невозможной.

И как и предполагается, либо вы будете использовать версии ниже 2.36, где выгрузка полноценно поддерживается, либо делаете собственный интерфейс для плагинов, и отказываетесь от модульных типов glib.

Ну или же ступаете на расточительность по памяти, когда плагин обработал, и более не требуется.

Я использовал версию 2.86.

→ Ссылка