Commit 2676a5e7 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

modules: introduce vlc_plugin_t, remove module_cache_t

This introduces a dedicated compound type for module containers
(i.e. plug-in shared libraries or statically linked module
descriptors), or plug-in. A plug-in can contain any number of
modules. The type describes plug-ins regardless of whence they came
from, libvlccore ("main"), static linking, dynamic linking or the
plug-ins cache.

Previously, plug-ins from the cache were described by module_cache_t,
only during the loading phase, while module_t was (ab)used to describe
loaded or cached plug-ins. Ultimately, that allows moving properties of
plug-ins out of the module structure where they do not really belong,
since there can be multiple modules per plug-ins.
parent f4bd7d54
......@@ -49,31 +49,32 @@
static struct
{
vlc_mutex_t lock;
module_t *head;
vlc_plugin_t *libs;
block_t *caches;
unsigned usage;
} modules = { VLC_STATIC_MUTEX, NULL, NULL, 0 };
static void module_StoreBank (module_t *module)
static void module_StoreBank(vlc_plugin_t *lib)
{
/*vlc_assert_locked (&modules.lock);*/
module->next = modules.head;
modules.head = module;
lib->next = modules.libs;
modules.libs = lib;
}
/**
* Registers a statically-linked plug-in.
*/
static module_t *module_InitStatic (vlc_plugin_cb entry)
static vlc_plugin_t *module_InitStatic(vlc_plugin_cb entry)
{
/* Initializes the module */
module_t *module = vlc_plugin_describe (entry);
if (unlikely(module == NULL))
/* Initializes the statically-linked library */
vlc_plugin_t *lib = vlc_plugin_describe (entry);
if (unlikely(lib == NULL))
return NULL;
module->b_loaded = true;
module->b_unloadable = false;
return module;
assert(lib->module != NULL);
lib->module->b_loaded = true;
lib->module->b_unloadable = false;
return lib;
}
#if defined(__ELF__) || !HAVE_DYNAMIC_PLUGINS
......@@ -89,10 +90,11 @@ static void module_InitStaticModules(void)
if (!vlc_static_modules)
return;
for (unsigned i = 0; vlc_static_modules[i]; i++) {
module_t *module = module_InitStatic (vlc_static_modules[i]);
if (likely(module != NULL))
module_StoreBank (module);
for (unsigned i = 0; vlc_static_modules[i]; i++)
{
vlc_plugin_t *lib = module_InitStatic(vlc_static_modules[i]);
if (likely(lib != NULL))
module_StoreBank(lib);
}
}
#else
......@@ -115,8 +117,8 @@ static void module_InitStaticModules(void) { }
* \param fast whether to optimize loading for speed or safety
* (fast is used when the plug-in is registered but not used)
*/
static module_t *module_InitDynamic (vlc_object_t *obj, const char *path,
bool fast)
static vlc_plugin_t *module_InitDynamic(vlc_object_t *obj, const char *path,
bool fast)
{
module_handle_t handle;
......@@ -134,8 +136,8 @@ static module_t *module_InitDynamic (vlc_object_t *obj, const char *path,
}
/* We can now try to call the symbol */
module_t *module = vlc_plugin_describe (entry);
if (unlikely(module == NULL))
vlc_plugin_t *plugin = vlc_plugin_describe(entry);
if (unlikely(plugin == NULL))
{
/* With a well-written module we shouldn't have to print an
* additional error message here, but just make sure. */
......@@ -143,15 +145,17 @@ static module_t *module_InitDynamic (vlc_object_t *obj, const char *path,
goto error;
}
module->psz_filename = strdup (path);
if (unlikely(module->psz_filename == NULL))
assert(plugin->module != NULL);
plugin->module->psz_filename = strdup (path);
if (unlikely(plugin->module->psz_filename == NULL))
{
vlc_module_destroy (module);
vlc_plugin_destroy(plugin);
goto error;
}
module->handle = handle;
module->b_loaded = true;
return module;
plugin->module->handle = handle;
plugin->module->b_loaded = true;
return plugin;
error:
module_Unload( handle );
return NULL;
......@@ -165,11 +169,9 @@ typedef struct module_bank
const char *base;
cache_mode_t mode;
size_t i_cache;
module_cache_t *cache;
size_t i_loaded_cache;
module_cache_t *loaded_cache;
size_t size;
vlc_plugin_t **plugins;
vlc_plugin_t *cache;
} module_bank_t;
/**
......@@ -178,30 +180,34 @@ typedef struct module_bank
static int AllocatePluginFile (module_bank_t *bank, const char *abspath,
const char *relpath, const struct stat *st)
{
module_t *module = NULL;
vlc_plugin_t *plugin = NULL;
/* Check our plugins cache first then load plugin if needed */
if (bank->mode == CACHE_USE)
{
module = CacheFind (bank->loaded_cache, bank->i_loaded_cache,
relpath, st);
if (module != NULL)
vlc_plugin_t *cache = vlc_cache_lookup(&bank->cache, relpath, st);
if (cache != NULL)
{
module->psz_filename = strdup (abspath);
if (unlikely(module->psz_filename == NULL))
{
vlc_module_destroy (module);
module = NULL;
}
assert(cache->module != NULL);
cache->module->psz_filename = strdup(abspath);
if (likely(cache->module->psz_filename != NULL))
plugin = cache;
else
vlc_plugin_destroy(cache);
}
}
if (module == NULL)
module = module_InitDynamic (bank->obj, abspath, true);
if (module == NULL)
if (plugin == NULL)
{
plugin = module_InitDynamic(bank->obj, abspath, true);
plugin->path = xstrdup(relpath);
plugin->mtime = st->st_mtime;
plugin->size = st->st_size;
}
if (plugin == NULL)
return -1;
/* We have not already scanned and inserted this module */
assert (module->next == NULL);
module_t *module = plugin->module;
assert(module != NULL);
/* For now we force loading if the module's config contains callbacks.
* Could be optimized by adding an API call.*/
......@@ -211,17 +217,19 @@ static int AllocatePluginFile (module_bank_t *bank, const char *abspath,
&& (module->p_config[i].list.psz_cb != NULL || module->p_config[i].list.i_cb != NULL))
{
/* !unloadable not allowed for plugins with callbacks */
vlc_module_destroy (module);
module = module_InitDynamic (bank->obj, abspath, false);
if (unlikely(module == NULL))
vlc_plugin_destroy(plugin);
assert(bank->mode != CACHE_RESET);
plugin = module_InitDynamic(bank->obj, abspath, false);
if (unlikely(plugin == NULL))
return -1;
break;
}
module_StoreBank (module);
module_StoreBank(plugin);
if (bank->mode != CACHE_IGNORE) /* Add entry to cache */
CacheAdd (&bank->cache, &bank->i_cache, relpath, st, module);
if (bank->mode != CACHE_IGNORE) /* Add entry to bank */
CacheAdd(&bank->plugins, &bank->size, plugin);
/* TODO: deal with errors */
return 0;
}
......@@ -318,8 +326,7 @@ static void AllocatePluginPath(vlc_object_t *obj, const char *path,
};
if (mode == CACHE_USE)
bank.i_loaded_cache = CacheLoad(bank.obj, bank.base,
&bank.loaded_cache, &modules.caches);
bank.cache = vlc_cache_load(obj, path, &modules.caches);
else
msg_Dbg(bank.obj, "ignoring plugins cache file");
......@@ -328,26 +335,19 @@ static void AllocatePluginPath(vlc_object_t *obj, const char *path,
/* Don't go deeper than 5 subdirectories */
AllocatePluginDir (&bank, 5, path, NULL);
switch( mode )
/* Discard unmatched cache entries */
while (bank.cache != NULL)
{
case CACHE_USE:
/* Discard unmatched cache entries */
for (size_t i = 0; i < bank.i_loaded_cache; i++)
{
if (bank.loaded_cache[i].p_module != NULL)
vlc_module_destroy(bank.loaded_cache[i].p_module);
free(bank.loaded_cache[i].path);
}
free(bank.loaded_cache);
for (size_t i = 0; i < bank.i_cache; i++)
free(bank.cache[i].path);
free(bank.cache);
break;
case CACHE_RESET:
CacheSave(obj, path, bank.cache, bank.i_cache);
case CACHE_IGNORE:
break;
vlc_plugin_t *plugin = bank.cache;
bank.cache = plugin->next;
vlc_plugin_destroy(plugin);
}
if (mode == CACHE_RESET)
CacheSave(obj, path, bank.plugins, bank.size);
free(bank.plugins);
}
/**
......@@ -421,9 +421,10 @@ void module_InitBank (void)
* library just as another module, and for instance the configuration
* options of core will be available in the module bank structure just
* as for every other module. */
module_t *module = module_InitStatic (vlc_entry__core);
if (likely(module != NULL))
module_StoreBank (module);
vlc_plugin_t *plugin = module_InitStatic(vlc_entry__core);
assert(plugin != NULL);
if (likely(plugin != NULL))
module_StoreBank(plugin);
config_SortConfig ();
}
modules.usage++;
......@@ -444,7 +445,7 @@ void module_InitBank (void)
*/
void module_EndBank (bool b_plugins)
{
module_t *head = NULL;
vlc_plugin_t *libs = NULL;
block_t *caches = NULL;
/* If plugins were _not_ loaded, then the caller still has the bank lock
......@@ -458,26 +459,27 @@ void module_EndBank (bool b_plugins)
if (--modules.usage == 0)
{
config_UnsortConfig ();
head = modules.head;
libs = modules.libs;
caches = modules.caches;
modules.head = NULL;
modules.libs = NULL;
modules.caches = NULL;
}
vlc_mutex_unlock (&modules.lock);
while (head != NULL)
while (libs != NULL)
{
module_t *module = head;
vlc_plugin_t *lib = libs;
head = module->next;
libs = lib->next;
#ifdef HAVE_DYNAMIC_PLUGINS
if (module->b_loaded && module->b_unloadable)
assert(lib->module != NULL);
if (lib->module->b_loaded && lib->module->b_unloadable)
{
module_Unload (module->handle);
module->b_loaded = false;
module_Unload(lib->module->handle);
lib->module->b_loaded = false;
}
#endif
vlc_module_destroy (module);
vlc_plugin_destroy(lib);
}
block_ChainRelease(caches);
......@@ -538,8 +540,11 @@ module_t **module_list_get (size_t *n)
assert (n != NULL);
for (module_t *mod = modules.head; mod; mod = mod->next)
for (vlc_plugin_t *lib = modules.libs; lib != NULL; lib = lib->next)
{
module_t *mod = lib->module;
assert(mod != NULL);
module_t **nt;
nt = realloc (tab, (i + 1 + mod->submodule_count) * sizeof (*tab));
if (unlikely(nt == NULL))
......@@ -581,8 +586,11 @@ ssize_t module_list_cap (module_t ***restrict list, const char *cap)
assert (list != NULL);
for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
for (vlc_plugin_t *lib = modules.libs; lib != NULL; lib = lib->next)
{
module_t *mod = lib->module;
assert(mod != NULL);
if (module_provides (mod, cap))
n++;
for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
......@@ -595,8 +603,11 @@ ssize_t module_list_cap (module_t ***restrict list, const char *cap)
if (unlikely(tab == NULL))
return -1;
for (module_t *mod = modules.head; mod != NULL; mod = mod->next)
for (vlc_plugin_t *lib = modules.libs; lib != NULL; lib = lib->next)
{
module_t *mod = lib->module;
assert(mod != NULL);
if (module_provides (mod, cap))
*(tab++)= mod;
for (module_t *subm = mod->submodule; subm != NULL; subm = subm->next)
......@@ -616,22 +627,25 @@ ssize_t module_list_cap (module_t ***restrict list, const char *cap)
int module_Map (vlc_object_t *obj, module_t *module)
{
static vlc_mutex_t lock = VLC_STATIC_MUTEX;
vlc_plugin_t *plugin = module->plugin;
if (module->parent != NULL)
module = module->parent;
assert(plugin != NULL);
module = plugin->module;
assert(module != NULL);
vlc_mutex_lock(&lock);
if (!module->b_loaded)
{
module_t *uncache;
vlc_plugin_t *uncache;
assert (module->psz_filename != NULL);
#ifdef HAVE_DYNAMIC_PLUGINS
uncache = module_InitDynamic (obj, module->psz_filename, false);
if (uncache != NULL)
{
CacheMerge (obj, module, uncache);
vlc_module_destroy (uncache);
assert(uncache->module != NULL);
CacheMerge(obj, module, uncache->module);
vlc_plugin_destroy(uncache);
}
else
#endif
......
......@@ -276,12 +276,13 @@ error:
return -1; /* FIXME: leaks */
}
static module_t *CacheLoadModule(block_t *file)
static module_t *vlc_cache_load_module(vlc_plugin_t *plugin, block_t *file)
{
module_t *module = vlc_module_create (NULL);
module_t *module = vlc_module_create(plugin);
if (unlikely(module == NULL))
return NULL;
plugin->module = module;
/* Load additional infos */
LOAD_STRING(module->psz_shortname);
LOAD_STRING(module->psz_longname);
......@@ -315,7 +316,7 @@ static module_t *CacheLoadModule(block_t *file)
for (; submodules > 0; submodules--)
{
module_t *submodule = vlc_module_create (module);
module_t *submodule = vlc_module_create(plugin);
free (submodule->pp_shortcuts);
LOAD_STRING(submodule->psz_shortname);
......@@ -342,6 +343,34 @@ error:
return NULL;
}
static vlc_plugin_t *vlc_cache_load_plugin(block_t *file)
{
vlc_plugin_t *plugin = malloc(sizeof (*plugin));
if (unlikely(plugin == NULL))
return NULL;
plugin->module = NULL;
if (vlc_cache_load_module(plugin, file) == NULL)
goto error;
const char *path;
LOAD_STRING(path);
if (path == NULL)
goto error;
plugin->path = strdup(path);
if (unlikely(plugin->path == NULL))
goto error;
LOAD_IMMEDIATE(plugin->mtime);
LOAD_IMMEDIATE(plugin->size);
return plugin;
error:
vlc_plugin_destroy(plugin);
return NULL;
}
/**
* Loads a plugins cache file.
*
......@@ -350,14 +379,13 @@ error:
* actually load the dynamically loadable module.
* This allows us to only fully load plugins when they are actually used.
*/
size_t CacheLoad(vlc_object_t *p_this, const char *dir, module_cache_t **r,
block_t **backingp)
vlc_plugin_t *vlc_cache_load(vlc_object_t *p_this, const char *dir,
block_t **backingp)
{
char *psz_filename;
assert( dir != NULL );
*r = NULL;
if( asprintf( &psz_filename, "%s"DIR_SEP CACHE_NAME, dir ) == -1 )
return 0;
......@@ -422,44 +450,28 @@ size_t CacheLoad(vlc_object_t *p_this, const char *dir, module_cache_t **r,
return 0;
}
module_cache_t *cache = NULL;
size_t count = 0;
vlc_plugin_t *cache = NULL;
for (;;)
while (file->i_buffer > 0)
{
module_t *module = CacheLoadModule (file);
if (module == NULL)
{
if (file->i_buffer == 0)
break;
vlc_plugin_t *plugin = vlc_cache_load_plugin(file);
if (plugin == NULL)
goto error;
}
const char *path;
struct stat st;
/* Load common info */
LOAD_STRING(path);
if (path == NULL)
goto error;
LOAD_IMMEDIATE(st.st_mtime);
LOAD_IMMEDIATE(st.st_size);
CacheAdd (&cache, &count, path, &st, module);
/* TODO: deal with errors */
plugin->next = cache;
cache = plugin;
}
file->p_next = *backingp;
*backingp = file;
*r = cache;
return count;
return cache;
error:
msg_Warn( p_this, "plugins cache not loaded (corrupted)" );
/* TODO: cleanup */
block_Release(file);
return 0;
return NULL;
}
#define SAVE_IMMEDIATE( a ) \
......@@ -590,8 +602,7 @@ error:
return -1;
}
static int CacheSaveBank (FILE *file, const module_cache_t *cache,
size_t i_cache)
static int CacheSaveBank(FILE *file, vlc_plugin_t *const *cache, size_t n)
{
uint32_t i_file_size = 0;
......@@ -614,9 +625,9 @@ static int CacheSaveBank (FILE *file, const module_cache_t *cache,
if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1)
goto error;
for (unsigned i = 0; i < i_cache; i++)
for (size_t i = 0; i < n; i++)
{
module_t *module = cache[i].p_module;
const module_t *module = cache[i]->module;
uint32_t i_submodule;
/* Save additional infos */
......@@ -643,9 +654,9 @@ static int CacheSaveBank (FILE *file, const module_cache_t *cache,
goto error;
/* Save common info */
SAVE_STRING(cache[i].path);
SAVE_IMMEDIATE(cache[i].mtime);
SAVE_IMMEDIATE(cache[i].size);
SAVE_STRING(cache[i]->path);
SAVE_IMMEDIATE(cache[i]->mtime);
SAVE_IMMEDIATE(cache[i]->size);
}
if (fflush (file)) /* flush libc buffers */
......@@ -659,8 +670,8 @@ error:
/**
* Saves a module cache to disk, and release cache data from memory.
*/
void CacheSave (vlc_object_t *p_this, const char *dir,
module_cache_t *entries, size_t n)
void CacheSave(vlc_object_t *p_this, const char *dir,
vlc_plugin_t *const *entries, size_t n)
{
char *filename = NULL, *tmpname = NULL;
......@@ -680,7 +691,7 @@ void CacheSave (vlc_object_t *p_this, const char *dir,
goto out;
}
if (CacheSaveBank (file, entries, n))
if (CacheSaveBank(file, entries, n))
{
msg_Warn (p_this, "cannot write %s: %s", tmpname,
vlc_strerror_c(errno));
......@@ -701,10 +712,6 @@ void CacheSave (vlc_object_t *p_this, const char *dir,
out:
free (filename);
free (tmpname);
for (size_t i = 0; i < n; i++)
free (entries[i].path);
free (entries);
}
/*****************************************************************************
......@@ -738,45 +745,43 @@ void CacheMerge( vlc_object_t *p_this, module_t *p_cache, module_t *p_module )
/**
* Looks up a plugin file in a table of cached plugins.
*/
module_t *CacheFind (module_cache_t *cache, size_t count,
const char *path, const struct stat *st)
vlc_plugin_t *vlc_cache_lookup(vlc_plugin_t **cache,
const char *path, const struct stat *st)
{
while (count > 0)
vlc_plugin_t **pp = cache, *plugin;
while ((plugin = *pp) != NULL)
{
if (cache->path != NULL
&& !strcmp (cache->path, path)
&& cache->mtime == st->st_mtime
&& cache->size == st->st_size)
{
module_t *module = cache->p_module;
cache->p_module = NULL;
return module;
}
cache++;
count--;
/* TODO: Preemptively delete plugins with matching name and different
* stats. This will save time in following look-ups. */
if (plugin->path != NULL
&& !strcmp(plugin->path, path)
&& plugin->mtime == st->st_mtime
&& plugin->size == st->st_size)
{
*pp = plugin->next;
plugin->next = NULL;
return plugin;
}
pp = &plugin->next;
}
return NULL;
}
/** Adds entry to the cache */
int CacheAdd (module_cache_t **cachep, size_t *countp,
const char *path, const struct stat *st, module_t *module)
int CacheAdd(vlc_plugin_t ***cachep, size_t *countp, vlc_plugin_t *plugin)
{
module_cache_t *cache = *cachep;
const size_t count = *countp;
vlc_plugin_t **cache = *cachep;
size_t count = *countp;
cache = realloc (cache, (count + 1) * sizeof (*cache));
cache = realloc(cache, (count + 1) * sizeof (*cache));
if (unlikely(cache == NULL))
return -1;
*cachep = cache;
cache += count;
/* NOTE: strdup() could be avoided, but it would be a bit ugly */
cache->path = strdup (path);
cache->mtime = st->st_mtime;
cache->size = st->st_size;
cache->p_module = module;
cache[count] = plugin;
*cachep = cache;
*countp = count + 1;
return 0;
}
......
......@@ -35,26 +35,24 @@
#include "config/configuration.h"
#include "libvlc.h"
module_t *vlc_module_create (module_t *parent)
module_t *vlc_module_create(vlc_plugin_t *plugin)
{
module_t *module = malloc (sizeof (*module));
if (module == NULL)
return NULL;
/* TODO: replace module/submodules with plugin/modules */
/* TODO: finish replacing module/submodules with plugin/modules */
module_t *parent = plugin->module;
if (parent == NULL)
{
module->next = NULL;
module->parent = NULL;
}
else
{
module->next = parent->submodule;
parent->submodule = module;
parent->submodule_count++;
module->parent = parent;
}
module->plugin = plugin;
module->submodule = NULL;
module->submodule_count = 0;
......@@ -80,8 +78,7 @@ module_t *vlc_module_create (module_t *parent)
}
/**
* Destroys a plug-in.
* @warning If the plug-in is loaded in memory, the handle will be leaked.
* Destroys a module.
*/
void vlc_module_destroy (module_t *module)
{
......@@ -99,6 +96,22 @@ void vlc_module_destroy (module_t *module)
free (module);
}
/**
* Destroys a plug-in.
* @warning If the plug-in was dynamically loaded in memory, the library handle
* and associated memory mappings and linker resources will be leaked.
*/
void vlc_plugin_destroy(vlc_plugin_t *plugin)
{
assert(plugin != NULL);
if (plugin->module != NULL)
vlc_module_destroy(plugin->module);
free(plugin->path);
free(plugin);
}
static module_config_t *vlc_config_create (module_t *module, int type)
{
unsigned confsize = module->confsize;
......@@ -141,9 +154,9 @@ static module_config_t *vlc_config_create (module_t *module, int type)
/**
* Callback for the plugin descriptor functions.
*/
static int vlc_plugin_setter (void *plugin, void *tgt, int propid, ...)
static int vlc_plugin_setter(void *ctx, void *tgt, int propid, ...)
{
module_t **pprimary = plugin;
vlc_plugin_t *plugin = ctx;
module_t *module = tgt;
module_config_t *item = tgt;
va_list ap;
......@@ -154,8 +167,8 @@ static int vlc_plugin_setter (void *plugin, void *tgt, int propid, ...)
{
case VLC_MODULE_CREATE:
{
module = *pprimary;
module_t *submodule = vlc_module_create (module);
module_t *module = plugin->module;
module_t *submodule = vlc_module_create(plugin);
if (unlikely(submodule == NULL))
{
ret = -1;
......@@ -163,11 +176,12 @@ static int vlc_plugin_setter (void *plugin, void *tgt, int propid, ...)
}
*(va_arg (ap, module_t **)) = submodule;
if (*pprimary == NULL)
if (module == NULL)
{
*pprimary = submodule;
plugin->module = submodule;
break;
}
/* Inheritance. Ugly!! */
submodule->pp_shortcuts = xmalloc (sizeof ( *submodule->pp_shortcuts ));
submodule->pp_shortcuts[0] = module->pp_shortcuts[0];
......@@ -184,7 +198,7 @@ static int vlc_plugin_setter (void *plugin, void *tgt, int propid, ...)
int type = va_arg (ap, int);
module_config_t **pp = va_arg (ap, module_config_t **);
item = vlc_config_create (*pprimary, type);
item = vlc_config_create(plugin->module, type);
if (unlikely(item == NULL))