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);
+}
 
 }
+
+}