Mercurial > hg > vamp-plugin-sdk
diff vamp-sdk/hostext/PluginLoader.cpp @ 59:fa79c4ec847d host-factory-stuff
* Put hostext stuff in the HostExt sub-namespace
* Tidy up system-specific stuff in PluginLoader
* Make PluginLoader return a deletion-notifying wrapper which permits the
library to be unloaded when no longer in use
* Add PluginChannelAdapter
* Make vamp-simple-host use PluginChannelAdapter, and use the PluginLoader
for plugin-running task. Also some other enhancements to host
author | cannam |
---|---|
date | Thu, 24 May 2007 15:17:07 +0000 |
parents | 0284955e31e5 |
children | 97c5ac99d725 |
line wrap: on
line diff
--- a/vamp-sdk/hostext/PluginLoader.cpp Thu May 24 10:05:00 2007 +0000 +++ b/vamp-sdk/hostext/PluginLoader.cpp Thu May 24 15:17:07 2007 +0000 @@ -34,19 +34,39 @@ authorization. */ +#include "vamp-sdk/PluginHostAdapter.h" #include "PluginLoader.h" -#include "PluginHostAdapter.h" - -#include "system.h" #include <fstream> -#include <dirent.h> // POSIX directory open and read +#ifdef _WIN32 + +#include <windows.h> +#include <tchar.h> +#define PLUGIN_SUFFIX "dll" + +#else /* ! _WIN32 */ + +#include <dirent.h> +#include <dlfcn.h> + +#ifdef __APPLE__ +#define PLUGIN_SUFFIX "dylib" +#else /* ! __APPLE__ */ +#define PLUGIN_SUFFIX "so" +#endif /* ! __APPLE__ */ + +#endif /* ! _WIN32 */ using namespace std; namespace Vamp { +namespace HostExt { + +PluginLoader * +PluginLoader::m_instance = 0; + PluginLoader::PluginLoader() { } @@ -55,73 +75,85 @@ { } +PluginLoader * +PluginLoader::getInstance() +{ + if (!m_instance) m_instance = new PluginLoader(); + return m_instance; +} + vector<PluginLoader::PluginKey> PluginLoader::listPlugins() { - if (m_pluginLibraryMap.empty()) { - - vector<string> path = PluginHostAdapter::getPluginPath(); - - size_t suffixLen = strlen(PLUGIN_SUFFIX); - - for (size_t i = 0; i < path.size(); ++i) { - - vector<string> files = getFilesInDir(path[i], PLUGIN_SUFFIX); - - - for (vector<string>::iterator fi = files.begin(); - fi != files.end(); ++fi) { - - string basename = *fi; - basename = basename.substr(0, basename.length() - suffixLen - 1); - - string fullPath = path[i]; - fullPath = fullPath + "/" + *fi; //!!! systemize - void *handle = DLOPEN(fullPath, RTLD_LAZY); - - if (!handle) { - cerr << "Vamp::PluginLoader: " << *fi - << ": unable to load library (" << DLERROR() - << ")" << endl; - continue; - } - - VampGetPluginDescriptorFunction fn = - (VampGetPluginDescriptorFunction)DLSYM - (handle, "vampGetPluginDescriptor"); - - if (!fn) { - DLCLOSE(handle); - continue; - } - - int index = 0; - const VampPluginDescriptor *descriptor = 0; - - while ((descriptor = fn(VAMP_API_VERSION, index))) { - PluginKey key = basename + ":" + descriptor->identifier; - if (m_pluginLibraryMap.find(key) == - m_pluginLibraryMap.end()) { - m_pluginLibraryMap[key] = fullPath; - } - ++index; - } - - DLCLOSE(handle); - } - } - } + if (m_pluginLibraryNameMap.empty()) generateLibraryMap(); vector<PluginKey> plugins; for (map<PluginKey, string>::iterator mi = - m_pluginLibraryMap.begin(); - mi != m_pluginLibraryMap.end(); ++mi) { + m_pluginLibraryNameMap.begin(); + mi != m_pluginLibraryNameMap.end(); ++mi) { plugins.push_back(mi->first); } return plugins; } +void +PluginLoader::generateLibraryMap() +{ + vector<string> path = PluginHostAdapter::getPluginPath(); + + for (size_t i = 0; i < path.size(); ++i) { + + vector<string> files = listFiles(path[i], PLUGIN_SUFFIX); + + for (vector<string>::iterator fi = files.begin(); + fi != files.end(); ++fi) { + + string fullPath = path[i]; + fullPath = splicePath(fullPath, *fi); + void *handle = loadLibrary(fullPath); + if (!handle) continue; + + VampGetPluginDescriptorFunction fn = + (VampGetPluginDescriptorFunction)lookupInLibrary + (handle, "vampGetPluginDescriptor"); + + if (!fn) { + unloadLibrary(handle); + continue; + } + + int index = 0; + const VampPluginDescriptor *descriptor = 0; + + while ((descriptor = fn(VAMP_API_VERSION, index))) { + PluginKey key = composePluginKey(*fi, descriptor->identifier); + if (m_pluginLibraryNameMap.find(key) == + m_pluginLibraryNameMap.end()) { + m_pluginLibraryNameMap[key] = fullPath; + } + ++index; + } + + unloadLibrary(handle); + } + } +} + +PluginLoader::PluginKey +PluginLoader::composePluginKey(string libraryName, string identifier) +{ + string basename = libraryName; + + string::size_type li = basename.rfind('/'); + if (li != string::npos) basename = basename.substr(li + 1); + + li = basename.find('.'); + if (li != string::npos) basename = basename.substr(0, li); + + return basename + ":" + identifier; +} + PluginLoader::PluginCategoryHierarchy PluginLoader::getPluginCategory(PluginKey plugin) { @@ -133,13 +165,13 @@ string PluginLoader::getLibraryPathForPlugin(PluginKey plugin) { - if (m_pluginLibraryMap.empty()) (void)listPlugins(); - if (m_pluginLibraryMap.find(plugin) == m_pluginLibraryMap.end()) return ""; - return m_pluginLibraryMap[plugin]; + if (m_pluginLibraryNameMap.empty()) generateLibraryMap(); + if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) return ""; + return m_pluginLibraryNameMap[plugin]; } Plugin * -PluginLoader::load(PluginKey key, float inputSampleRate) +PluginLoader::loadPlugin(PluginKey key, float inputSampleRate) { string fullPath = getLibraryPathForPlugin(key); if (fullPath == "") return 0; @@ -152,23 +184,15 @@ string identifier = key.substr(ki + 1); - void *handle = DLOPEN(fullPath, RTLD_LAZY); - - if (!handle) { - cerr << "Vamp::PluginLoader: " << fullPath - << ": unable to load library (" << DLERROR() - << ")" << endl; - return 0; - } + void *handle = loadLibrary(fullPath); + if (!handle) return 0; VampGetPluginDescriptorFunction fn = - (VampGetPluginDescriptorFunction)DLSYM + (VampGetPluginDescriptorFunction)lookupInLibrary (handle, "vampGetPluginDescriptor"); if (!fn) { - //!!! refcount this! --!!! no, POSIX says dlopen/dlclose will - // reference count. check on win32 - DLCLOSE(handle); + unloadLibrary(handle); return 0; } @@ -176,46 +200,29 @@ const VampPluginDescriptor *descriptor = 0; while ((descriptor = fn(VAMP_API_VERSION, index))) { + if (string(descriptor->identifier) == identifier) { - return new Vamp::PluginHostAdapter(descriptor, inputSampleRate); + + Vamp::PluginHostAdapter *plugin = + new Vamp::PluginHostAdapter(descriptor, inputSampleRate); + + PluginDeletionNotifyAdapter *adapter = + new PluginDeletionNotifyAdapter(plugin, this); + + m_pluginLibraryHandleMap[adapter] = handle; + return adapter; } + ++index; } - - //!!! flag error + + cerr << "Vamp::HostExt::PluginLoader: Plugin \"" + << identifier << "\" not found in library \"" + << fullPath << "\"" << endl; + return 0; } -vector<string> -PluginLoader::getFilesInDir(string dir, string extension) -{ - vector<string> files; - - DIR *d = opendir(dir.c_str()); - if (!d) return files; - - struct dirent *e = 0; - while ((e = readdir(d))) { - - if (!(e->d_type & DT_REG) || !e->d_name) { - continue; - } - - int len = strlen(e->d_name); - if (len < int(extension.length() + 2) || - e->d_name[len - extension.length() - 1] != '.' || - strcmp(e->d_name + len - extension.length(), extension.c_str())) { - continue; - } - - files.push_back(e->d_name); - } - - closedir(d); - - return files; -} - void PluginLoader::generateTaxonomy() { @@ -229,6 +236,11 @@ for (vector<string>::iterator i = path.begin(); i != path.end(); ++i) { + + // It doesn't matter that we're using literal forward-slash in + // this bit, as it's only relevant if the path contains + // "/lib/", which is only meaningful and only plausible on + // systems with forward-slash delimiters string dir = *i; string::size_type li = dir.find(libfragment); @@ -248,12 +260,12 @@ for (vector<string>::iterator i = catpath.begin(); i != catpath.end(); ++i) { - vector<string> files = getFilesInDir(*i, suffix); + vector<string> files = listFiles(*i, suffix); for (vector<string>::iterator fi = files.begin(); fi != files.end(); ++fi) { - string filepath = *i + "/" + *fi; //!!! systemize + string filepath = splicePath(*i, *fi); ifstream is(filepath.c_str(), ifstream::in | ifstream::binary); if (is.fail()) { @@ -299,5 +311,130 @@ } } +void * +PluginLoader::loadLibrary(string path) +{ + void *handle = 0; +#ifdef _WIN32 + handle = LoadLibrary(path.c_str()); + if (!handle) { + cerr << "Vamp::HostExt::PluginLoader: Unable to load library \"" + << path << "\"" << endl; + } +#else + handle = dlopen(path.c_str(), RTLD_LAZY); + if (!handle) { + cerr << "Vamp::HostExt::PluginLoader: Unable to load library \"" + << path << "\": " << dlerror() << endl; + } +#endif + return handle; +} + +void +PluginLoader::unloadLibrary(void *handle) +{ +#ifdef _WIN32 + FreeLibrary((HINSTANCE)handle); +#else + dlclose(handle); +#endif +} + +void * +PluginLoader::lookupInLibrary(void *handle, const char *symbol) +{ +#ifdef _WIN32 + return (void *)GetProcAddress((HINSTANCE)handle, symbol); +#else + return (void *)dlsym(handle, symbol); +#endif +} + +string +PluginLoader::splicePath(string a, string b) +{ +#ifdef _WIN32 + return a + "\\" + b; +#else + return a + "/" + b; +#endif +} + +vector<string> +PluginLoader::listFiles(string dir, string extension) +{ + vector<string> files; + size_t extlen = extension.length(); + +#ifdef _WIN32 + + string expression = dir + "\\*." + extension; + WIN32_FIND_DATA data; + HANDLE fh = FindFirstFile(expression.c_str(), &data); + if (fh == INVALID_HANDLE_VALUE) return files; + + bool ok = true; + while (ok) { + files.push_back(data.cFileName); + ok = FindNextFile(fh, &data); + } + + FindClose(fh); + +#else + DIR *d = opendir(dir.c_str()); + if (!d) return files; + + struct dirent *e = 0; + while ((e = readdir(d))) { + + if (!(e->d_type & DT_REG) || !e->d_name) continue; + + size_t len = strlen(e->d_name); + if (len < extlen + 2 || + e->d_name + len - extlen - 1 != "." + extension) { + continue; + } + + files.push_back(e->d_name); + } + + closedir(d); +#endif + + return files; +} + +void +PluginLoader::pluginDeleted(PluginDeletionNotifyAdapter *adapter) +{ + void *handle = m_pluginLibraryHandleMap[adapter]; + if (handle) unloadLibrary(handle); + m_pluginLibraryHandleMap.erase(adapter); +} + +PluginLoader::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin, + PluginLoader *loader) : + PluginWrapper(plugin), + m_loader(loader) +{ +} + +PluginLoader::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter() +{ + // We need to delete the plugin before calling pluginDeleted, as + // the delete call may require calling through to the descriptor + // (for e.g. cleanup) but pluginDeleted may unload the required + // library for the call. To prevent a double deletion when our + // parent's destructor runs (after this one), be sure to set + // m_plugin to 0 after deletion. + delete m_plugin; + m_plugin = 0; + + if (m_loader) m_loader->pluginDeleted(this); +} } + +}