Make modules more flexible (builtin or loaded are identical). Add a test module
[lttv.git] / ltt / branches / poly / lttv / main / module.c
index df98aacf7fe1426370523fa76dc75c61885a60c5..c4d3d6b07405c037a444cd11058e42cea2de876a 100644 (file)
@@ -1,3 +1,4 @@
+
 /* This file is part of the Linux Trace Toolkit viewer
  * Copyright (C) 2003-2004 Michel Dagenais
  *
  */
 
 
-/* module.c : Implementation of the module loading/unloading mechanism.
- * 
- */
+/* module.c : Implementation of the module loading/unloading mechanism. */
 
 #include <lttv/module.h>
+#include <gmodule.h>
+
+
+struct _LttvLibrary
+{
+  LttvLibraryInfo info;
+  GPtrArray *modules;
+  GModule *gm;
+  guint locked_loaded;
+};
+
 
 struct _LttvModule
 {
-  GModule *module;
-  guint ref_count;
-  guint load_count;
-  GPtrArray *dependents;
+  LttvModuleInfo info;
+  char **prerequisites_names;
+  GPtrArray *prerequisites;
 };
 
 
-/* Table of loaded modules and paths where to search for modules */
+/* Modules are searched by name. However, a library may be loaded which
+   provides a module with the same name as an existing one. A stack of
+   modules is thus maintained for each name. 
+
+   Libraries correspond to glib modules. The g_module function is 
+   responsible for loading each library only once. */
+
+static GHashTable *modules_by_name = NULL;
 
-static GHashTable *modules = NULL;
+static GPtrArray *libraries = NULL;
 
-static GPtrArray *modulesPaths = NULL;
+static GHashTable *libraries_by_g_module = NULL;
 
-static void lttv_module_unload_all();
+static GPtrArray *library_paths = NULL;
 
+static gboolean initialized = FALSE;
 
-void lttv_module_init(int argc, char **argv) 
+static gboolean destroyed = TRUE;
+
+static struct _LttvModuleDescription *builtin_chain = NULL;
+
+static struct _LttvModuleDescription *module_chain = NULL;
+
+static struct _LttvModuleDescription **module_next = &module_chain;
+
+static GQuark lttv_module_error;
+
+static void init();
+
+static finish_destroy();
+
+static void module_release(LttvModule *m);
+
+
+static LttvLibrary *library_add(char *name, char *path, GModule *gm)
 {
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init module.c");
-  modules = g_hash_table_new(g_str_hash, g_str_equal);
-  modulesPaths = g_ptr_array_new();
-}
+  LttvLibrary *l;
 
+  LttvModule *m;
 
-void lttv_module_destroy() 
-{  
-  int i;
+  struct _LttvModuleDescription *link;
 
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy module.c");
+  GPtrArray *modules;
+
+  l = g_new(LttvLibrary, 1);
+  l->modules = g_ptr_array_new();
+  l->gm = gm;
+  l->locked_loaded = 0;
+  l->info.name = g_strdup(name);
+  l->info.path = g_strdup(path);
+  l->info.load_count = 0;
 
-  /* Unload all modules */
-  lttv_module_unload_all();
+  g_ptr_array_add(libraries, l);
+  g_hash_table_insert(libraries_by_g_module, gm, l);
 
-  /* Free the modules paths pointer array as well as the elements */
-  for(i = 0; i < modulesPaths->len ; i++) {
-    g_free(modulesPaths->pdata[i]);
+  *module_next = NULL;
+  for(link = module_chain; link != NULL; link = link->next) {
+    m = g_new(LttvModule, 1);
+    g_ptr_array_add(l->modules, m);
+
+    modules = g_hash_table_lookup(modules_by_name, link->name);
+    if(modules == NULL) {
+      modules = g_ptr_array_new();
+      g_hash_table_insert(modules_by_name, g_strdup(link->name), modules);
+    }
+    g_ptr_array_add(modules, m);
+
+    m->prerequisites_names = link->prerequisites;
+    m->prerequisites = g_ptr_array_new();
+    m->info.name = link->name;
+    m->info.short_description = link->short_description;
+    m->info.description = link->description;
+    m->info.init = link->init;
+    m->info.destroy = link->destroy;
+    m->info.library = l;
+    m->info.require_count = 0;
+    m->info.use_count = 0;
+    m->info.prerequisites_number = link->prerequisites_number;
   }
-  g_ptr_array_free(modulesPaths,TRUE) ;
-  modulesPaths = NULL;
-  
-  /* destroy the hash table */
-  g_hash_table_destroy(modules) ;
-  modules = NULL;
+  return l;
 }
 
 
-/* Add a new pathname to the modules loading search path */
-
-void lttv_module_path_add(const char *name) 
+static void library_remove(LttvLibrary *l)
 {
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add module path %s", name);
-  g_ptr_array_add(modulesPaths,(char*)g_strdup(name));
+  LttvModule *m;
+
+  GPtrArray *modules;
+
+  guint i;
+
+  char *key;
+
+  for(i = 0 ; i < l->modules->len ; i++) {
+    m = (LttvModule *)(l->modules->pdata[i]);
+
+    g_hash_table_lookup_extended(modules_by_name, m->info.name, 
+                                (gpointer *)&key, (gpointer *)&modules);
+    g_assert(modules != NULL);
+    g_ptr_array_remove(modules, m);
+    if(modules->len == 0) {
+      g_hash_table_remove(modules_by_name, m->info.name);
+      g_ptr_array_free(modules, TRUE);
+      g_free(key);
+    }
+
+    g_ptr_array_free(m->prerequisites, TRUE);
+    g_free(m);
+  }
+
+  g_ptr_array_remove(libraries, l);
+  g_hash_table_remove(libraries_by_g_module, l->gm);
+  g_ptr_array_free(l->modules, TRUE);
+  g_free(l->info.name);
+  g_free(l->info.path);
+  g_free(l);
 }
 
 
-static LttvModule *
-module_load(const char *name, int argc, char **argv) 
+static LttvLibrary *library_load(char *name, GError **error)
 {
   GModule *gm;
 
-  LttvModule *m;
+  int i, nb;
+
+  char *path, *pathname;
+
+  LttvLibrary *l;
 
-  int i;
+  GString *messages = g_string_new("");
 
-  char *pathname;
+  /* insure that module.c is initialized */
 
-  const char *module_name;
-  
-  LttvModuleInit init_function;
+  init();
 
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load module %s", name);
+  /* Try to find the library along all the user specified paths */
 
-  /* Try to find the module along all the user specified paths */
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load library %s", name);
+  nb = lttv_library_path_number();
+  for(i = 0 ; i <= nb ; i++) {
+    if(i < nb) path = lttv_library_path_get(i);
+    else path = NULL;
 
-  for(i = 0 ; i < modulesPaths->len ; i++) {
-    pathname = g_module_build_path(modulesPaths->pdata[i],name);
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Try path %s", pathname);
+    pathname = g_module_build_path(path ,name);
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Try path %s", pathname);
+    module_chain = NULL;
+    module_next = &module_chain;
     gm = g_module_open(pathname,0);
     g_free(pathname);    
     
     if(gm != NULL) break;
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,"Trial failed, %s",g_module_error());
-  }
-
-  /* Try the default system path */
 
-  if(gm == NULL) {
-    pathname = g_module_build_path(NULL,name);
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Try default path");
-    gm = g_module_open(pathname,0);
-    g_free(pathname);
+    g_string_append(messages, g_module_error());
+    g_string_append(messages, "\n");
+    g_log(G_LOG_DOMAIN,G_LOG_LEVEL_INFO,"Trial failed, %s", g_module_error());
   }
 
   /* Module cannot be found */
+
   if(gm == NULL) {
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,"Trial failed, %s",g_module_error());
-    g_warning("Failed to load module %s", name); 
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Failed to load %s", name); 
+    g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
+          "Cannot load library %s: %s", name, messages->str);
+    g_string_free(messages, TRUE);
     return NULL;
   }
+  g_string_free(messages, TRUE);
+
+  /* Check if the library was already loaded */
+
+  l = g_hash_table_lookup(libraries_by_g_module, gm);
 
-  /* Check if the module was already opened using the hopefully canonical name
-     returned by g_module_name. */
+  /* This library was not already loaded */
 
-  module_name = g_module_name(gm);
+  if(l == NULL) {
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Library %s (%s) loaded", name, 
+        g_module_name(gm));
+    l = library_add(name, path, gm);
+  }
+  return l;
+}
 
-  m = g_hash_table_lookup(modules, module_name);
 
-  if(m == NULL) {
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, 
-        "Module %s (%s) loaded, call its init function", name, module_name);
+LttvLibrary *lttv_library_load(char *name, GError **error)
+{
+  LttvLibrary *l = library_load(name, error); 
+  l->info.load_count++;
+  return l;
+}
 
-    /* Module loaded for the first time. Insert it in the table and call the
-       init function if any. */
 
-    m = g_new(LttvModule, 1);
-    m->module = gm;
-    m->ref_count = 0;
-    m->load_count = 0;
-    m->dependents = g_ptr_array_new();
-    g_hash_table_insert(modules, (gpointer)module_name, m);
-
-    if(!g_module_symbol(gm, "init", (gpointer)&init_function)) {
-      g_warning("module %s (%s) has no init function", name, pathname);
-    }
-    else init_function(m, argc, argv);
+static void library_unload(LttvLibrary *l)
+{
+  guint i, len;
+
+  GModule *gm;
+
+  LttvModule *m;
+
+  if(l->locked_loaded > 0) {
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: locked loaded", 
+        l->info.name);
+    return;
+  }
+
+  if(l->info.load_count > 0) {
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: load count %d", 
+       l->info.name, l->info.load_count);
+    return;
   }
-  else {
 
-    /* Module was already opened, check that it really is the same and
-       undo the extra g_module_open */
+  /* Check if all its modules have been released */
 
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, 
-        "Module %s (%s) was already loaded, no need to call init function",
-       name, module_name);
-    if(m->module != gm) g_error("Two gmodules with the same pathname");
-    g_module_close(gm);
+  for(i = 0 ; i < l->modules->len ; i++) {
+    m = (LttvModule *)(l->modules->pdata[i]);
+    if(m->info.use_count > 0) {
+      g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,"Unload library %s: module %s used",
+          l->info.name, m->info.name);
+      return;
+    }
   }
-  m->ref_count++;
-  return m;
+
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: close the GModule",
+       l->info.name);
+  gm = l->gm;
+  library_remove(l);
+  if(gm != NULL) g_module_close(gm);
+
+  /* insure that module.c will be finalized */
+
+  finish_destroy();
 }
 
 
-LttvModule *
-lttv_module_load(const char *name, int argc, char **argv) 
+void lttv_library_unload(LttvLibrary *l)
 {
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load module %s explicitly", name);
-  LttvModule *m = module_load(name, argc, argv);
-  if(m != NULL) m->load_count++;
-  return m;
+  l->info.load_count--;
+  library_unload(l);
 }
 
 
-LttvModule *
-lttv_module_require(LttvModule *m, const char *name, int argc, char **argv)
+static void library_lock_loaded(LttvLibrary *l)
 {
-  LttvModule *module;
-
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, 
-      "Load module %s, as %s is a dependent requiring it", name, 
-      g_module_name(m->module));
-  module = module_load(name, argc, argv);
-  if(module != NULL) g_ptr_array_add(m->dependents, module);
-  return module;
+  l->locked_loaded++;
 }
 
 
-static void module_unload(LttvModule *m) 
+static void library_unlock_loaded(LttvLibrary *l)
 {
-  LttvModuleDestroy destroy_function;
+  l->locked_loaded--;
+  library_unload(l);
+}
 
-  char *pathname;
 
-  guint i, len;
+static LttvModule *module_require(char *name, GError **error)
+{
+  GError *tmp_error = NULL;
 
-  /* Decrement the reference count */
+  guint i, j;
 
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload module %s", 
-      g_module_name(m->module));
-  m->ref_count--;
-  if(m->ref_count > 0) {
-    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, 
-        "Module usage count decremented to %d", m->ref_count);
-    return;
-  }
+  LttvModule *m, *required;
+
+  LttvLibrary *l = NULL;
+
+  GPtrArray *modules;
+
+  /* Insure that module.c is initialized */
+
+  init();
 
-  /* We really have to unload the module. First destroy it. */
+  /* Check if the module is already loaded */
 
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, 
-      "Call the destroy function and unload the module");
-  if(!g_module_symbol(m->module, "destroy", (gpointer)&destroy_function)) {
-    g_warning("module (%s) has no destroy function", pathname);
+  modules = g_hash_table_lookup(modules_by_name, name);
+
+  /* Try to load a library having the module name */
+
+  if(modules == NULL) {
+    l = library_load(name, error);
+    if(l == NULL) return NULL;
+    else library_lock_loaded(l);
+
+    /* A library was found, does it contain the named module */
+
+    modules = g_hash_table_lookup(modules_by_name, name);
+    if(modules == NULL) {
+      g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
+          "Module %s not found in library %s", name, l->info.name);
+      library_unlock_loaded(l);
+      return NULL;
+    }
   }
-  else destroy_function();
+  m = (LttvModule *)(modules->pdata[modules->len - 1]);
 
-  /* Then release the modules required by this module */
+  /* We have the module */
 
-  len = m->dependents->len;
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload dependent modules");
+  m->info.use_count++;
 
-  for(i = 0 ; i < len ; i++) {
-    module_unload(m->dependents->pdata[i]);
+  /* First use of the module. Initialize after getting the prerequisites */
+
+  if(m->info.use_count == 1) {
+    for(i = 0 ; i < m->info.prerequisites_number ; i++) {
+      required = module_require(m->prerequisites_names[i], &tmp_error);
+
+      /* A prerequisite could not be found, undo everything and fail */
+
+      if(required == NULL) {
+        for(j = 0 ; j < m->prerequisites->len ; j++) {
+          module_release((LttvModule *)(m->prerequisites->pdata[j]));
+        }
+        g_ptr_array_set_size(m->prerequisites, 0);
+        if(l != NULL) library_unlock_loaded(l);
+        g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
+            "Cannot find prerequisite for module %s: %s", name, 
+           tmp_error->message);
+        g_clear_error(&tmp_error);
+        return NULL;
+      }
+      g_ptr_array_add(m->prerequisites, required);
+    }
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: init()", m->info.name);
+    m->info.init();
   }
 
-  if(len != m->dependents->len) g_error("dependents list modified");
+  /* Decrement the load count of the library. It will not really be 
+     unloaded since it contains a currently used module. */
+
+  if(l != NULL) library_unlock_loaded(l);
 
-  /* Finally remove any trace of this module */
+  return(m);
+}
+
+
+/* The require_count for a module is the number of explicit calls to 
+   lttv_module_require, while the use_count also counts the number of times
+   a module is needed as a prerequisite. */
 
-  g_hash_table_remove(modules, g_module_name(m->module));
-  g_ptr_array_free(m->dependents, TRUE);
-  g_module_close(m->module);
-  g_free(m);
+LttvModule *lttv_module_require(char *name, GError **error)
+{
+  LttvModule *m = module_require(name, error);
+  if(m != NULL) m->info.require_count++;
+  return(m);
 }
 
 
-void lttv_module_unload(LttvModule *m) 
+static void module_release(LttvModule *m)
 {
-  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Explicitly unload module %s", 
-      g_module_name(m->module));
-  if(m->load_count <= 0) { 
-    g_error("more unload than load (%s)", g_module_name(m->module));
-    return;
+  guint i;
+
+  library_lock_loaded(m->info.library);
+
+  m->info.use_count--;
+  if(m->info.use_count == 0) {
+    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: destroy()",m->info.name);
+    m->info.destroy();
+    for(i = 0 ; i < m->prerequisites->len ; i++) {
+      module_release((LttvModule *)(m->prerequisites->pdata[i]));
+    }
+    g_ptr_array_set_size(m->prerequisites, 0);
   }
-  m->load_count--;
-  module_unload(m);
+  library_unlock_loaded(m->info.library);
+}
+
+
+void lttv_module_release(LttvModule *m)
+{
+  m->info.require_count--;
+  module_release(m);
+}
+
+
+void lttv_module_info(LttvModule *m, LttvModuleInfo *info)
+{
+  *info = m->info;
+}
+
+
+unsigned lttv_module_prerequisite_number(LttvModule *m)
+{
+  return m->prerequisites->len;
+}
+
+
+LttvModule *lttv_module_prerequisite_get(LttvModule *m, unsigned i)
+{
+  return (LttvModule *)(m->prerequisites->pdata[i]);
+}
+
+
+void lttv_library_info(LttvLibrary *l, LttvLibraryInfo *info)
+{
+  *info = l->info;
+}
+
+
+unsigned lttv_library_module_number(LttvLibrary *l)
+{
+  return l->modules->len;
+}
+
+
+LttvModule *lttv_library_module_get(LttvLibrary *l, unsigned i)
+{
+  return (LttvModule *)(l->modules->pdata[i]);
+}
+
+
+unsigned lttv_library_number()
+{
+  return libraries->len;  
 }
 
 
-static void
-list_modules(gpointer key, gpointer value, gpointer user_data)
+LttvLibrary *lttv_library_get(unsigned i)
 {
-  g_ptr_array_add((GPtrArray *)user_data, value);
+  return (LttvLibrary *)(libraries->pdata[i]);
 }
 
 
-LttvModule **
-lttv_module_list(guint *nb)
+void lttv_library_path_add(char *name)
 {
-  GPtrArray *list = g_ptr_array_new();
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add library path %s", name);
+  g_ptr_array_add(library_paths,(char*)g_strdup(name));
+}
+
 
-  LttvModule **array;
+void lttv_library_path_remove(char *name) 
+{
+  guint i;
 
-  g_hash_table_foreach(modules, list_modules, list);
-  *nb = list->len;
-  array = (LttvModule **)list->pdata;
-  g_ptr_array_free(list, FALSE);
-  return array;
+  for(i = 0 ; i < library_paths->len ; i++) {
+    if(g_str_equal(name, library_paths->pdata[i])) {
+      g_free(library_paths->pdata[i]);
+      g_ptr_array_remove_index(library_paths,i);
+      return;
+    }
+  }
 }
 
 
-LttvModule **
-lttv_module_info(LttvModule *m, const char **name, 
-    guint *ref_count, guint *load_count, guint *nb_dependents)
+unsigned lttv_library_path_number()
 {
-  guint i, len = m->dependents->len;
+  return library_paths->len;
+}
 
-  LttvModule **array = g_new(LttvModule *, len);
 
-  *name = g_module_name(m->module);
-  *ref_count = m->ref_count;
-  *load_count = m->load_count;
-  *nb_dependents = len;
-  for(i = 0 ; i < len ; i++) array[i] = m->dependents->pdata[i];
-  return array;
+char *lttv_library_path_get(unsigned i)
+{
+  return (char *)(library_paths->pdata[library_paths->len - i - 1]);
 }
 
-char * 
-lttv_module_name(LttvModule *m)
+
+void lttv_module_register(struct _LttvModuleDescription *d)
 {
-  return g_module_name(m->module);
+  *module_next = d;
+  module_next = &(d->next);
 }
 
-static void
-list_independent(gpointer key, gpointer value, gpointer user_data)
+
+static void init() 
 {
-  LttvModule *m = (LttvModule *)value;
+  if(initialized) return;
+  g_assert(destroyed);
+
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init module.c");
 
-  if(m->load_count > 0) g_ptr_array_add((GPtrArray *)user_data, m);
+  initialized = TRUE;
+  destroyed = FALSE;
+  lttv_module_error = g_quark_from_string("LTTV_MODULE_ERROR");
+  modules_by_name = g_hash_table_new(g_str_hash, g_str_equal);
+  libraries = g_ptr_array_new();
+  libraries_by_g_module = g_hash_table_new(g_direct_hash, g_direct_equal);
+  library_paths = g_ptr_array_new();
+
+  if(builtin_chain == NULL) builtin_chain = module_chain;
+  module_chain = builtin_chain;
+  library_add("builtin", NULL, NULL);
 }
 
 
-void
-lttv_module_unload_all()
+static finish_destroy()
 {
   guint i;
 
+  if(initialized) return;
+  g_assert(!destroyed);
+
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Finish destroy module.c");
+  g_hash_table_destroy(modules_by_name);
+  g_ptr_array_free(libraries, TRUE);
+  g_hash_table_destroy(libraries_by_g_module);
+  for(i = 0 ; i < library_paths->len ; i++) {
+    g_free(library_paths->pdata[i]);
+  }
+  g_ptr_array_free(library_paths, TRUE);
+  destroyed = TRUE;
+}
+
+
+static void destroy() 
+{  
+  guint i, j, nb;
+
+  LttvLibrary *l, **locked_libraries;
+
   LttvModule *m;
 
-  GPtrArray *independent_modules;
+  g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy module.c");
+
+  /* Unload all libraries */
 
-  while(g_hash_table_size(modules) != 0) {
-    independent_modules = g_ptr_array_new();
-    g_hash_table_foreach(modules, list_independent, independent_modules);
+  nb = libraries->len;
+  locked_libraries = g_new(LttvLibrary *, nb);
 
-    for(i = 0 ; i < independent_modules->len ; i++) {
-      m = (LttvModule *)independent_modules->pdata[i];
-      lttv_module_unload(m);
+  for(i = 0 ; i < nb ; i++) {
+    l = (LttvLibrary *)(libraries->pdata[i]);
+    locked_libraries[i] = l;
+    library_lock_loaded(l);
+    for(j = 0 ; j < l->modules->len ; j++) {
+      m = (LttvModule *)(l->modules->pdata[j]);
+      while(m->info.require_count > 0) lttv_module_release(m);
     }
-    g_ptr_array_free(independent_modules, TRUE);
+    while(l->info.load_count > 0) lttv_library_unload(l);
+  }
+
+  for(i = 0 ; i < nb ; i++) {
+    l = locked_libraries[i];
+    library_unlock_loaded(l);
   }
+  g_free(locked_libraries);
+
+  /* The library containing module.c may be locked by our caller */
+
+  g_assert(libraries->len <= 1); 
+
+  initialized = FALSE;
 }
+
+LTTV_MODULE("module", "Modules in libraries",                        \
+    "Load libraries, list, require and initialize contained modules", \
+    init, destroy)
+
This page took 0.034611 seconds and 4 git commands to generate.