]> Dogcows Code - chaz/yoink/blobdiff - src/moof/resource.cc
fixed documentation about where to find licenses
[chaz/yoink] / src / moof / resource.cc
index ac200f7831e380866665bc490a48ed416340a7e7..8188313b8442011b615925cd61e1aafcf6b81260 100644 (file)
@@ -1,13 +1,15 @@
 
-/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+/*]  Copyright (c) 2009-2011, Charles McGarvey  [*****************************
 **]  All rights reserved.
 *
-* vi:ts=4 sw=4 tw=75
-*
 * Distributable under the terms and conditions of the 2-clause BSD license;
 * see the file COPYING for a complete text of the license.
 *
-**************************************************************************/
+*****************************************************************************/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
 
 #include <queue>
 
 
 #include <stlplus/portability/file_system.hpp>
 
-#include "hash.hh"
-#include "resource.hh"
-
-#if HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#if USE_HOTLOADING
+#if ENABLE_HOTLOADING
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #endif
 
+#include "hash.hh"
+#include "log.hh"
+#include "resource.hh"
+
 #ifndef BUF_SIZE
-#define BUF_SIZE 4096
+#define BUF_SIZE 8192
 #endif
 
 
 namespace moof {
-       
-
-static std::vector<std::string> search_paths_;
 
-typedef boost::weak_ptr<resource> resource_weakptr;
-static hash<std::string,resource_weakptr,hash_function> resource_table_;
 
-// static member
-resource::type_lookup_ptr resource::type_lookup_;
+/*] Filesystem searching
+ *************************************************************************/
 
+static std::vector<std::string> search_paths_;
 
-#if USE_HOTLOADING
-static hash<int,std::string,hash_function> monitor_lookup_;
-static int monitor_fd_ = inotify_init1(IN_NONBLOCK);
-#endif
+void resource::set_search_paths(const std::string& paths)
+{
+       boost::split(search_paths_, paths, boost::is_any_of(":"));
+}
 
-int resource::reload_as_needed()
+std::string resource::find_file(const std::string& name)
 {
-       int num_resources = 0;
+       std::string ext = stlplus::extension_part(name);
+       std::string prefix;
+       std::string path;
 
-#if USE_HOTLOADING
-       log_info("hotloading?");
-       char bytes[BUF_SIZE];
-       int num_bytes;
-       if (0 < (num_bytes = read(monitor_fd_, bytes, num_bytes)))
+       loader_ptr loader;
+       call_registry(ext, loader, lookup);
+       if (loader) prefix = loader->prefix();
+
+       std::vector<std::string>::iterator it;
+       for (it = search_paths_.begin(); it != search_paths_.end(); ++it)
        {
-               char* end = bytes + num_bytes;
-               char* byte = bytes;
+               if (!prefix.empty())
+               {
+                       // try it with the prefix first
+                       path = stlplus::filespec_to_path(*it, prefix);
+                       path = stlplus::filespec_to_path(path, name);
+                       log_debug("looking for", name, "at", path);
+                       if (stlplus::file_exists(path)) return path;
+               }
 
-               log_warning("num_bytes:", num_bytes);
-               log_error("1");
+               path = stlplus::filespec_to_path(*it, name);
+               log_debug("looking for", name, "at", path);
+               if (stlplus::file_exists(path)) return path;
+       }
 
-               while (byte < end)
-               {
-                       struct inotify_event* event = (struct inotify_event*)byte;
+       log_error("cannot find resource file:", name);
+       return std::string();
+}
 
-                       if (event->mask & IN_IGNORED)
-                       {
-                               log_warning("watch", event->wd, "removed");
-                       }
+std::string
+resource::find_file(const std::string& name, const std::string& ext)
+{
+       std::string actual_ext = stlplus::extension_part(name);
+       if (actual_ext != ext)
+       {
+               // try the explicit extension first
+               return find_file(stlplus::create_filename(name, ext));
+       }
+       return find_file(name);
+}
 
-                       log_error("2");
-                       hash<int,std::string,hash_function>::iterator it;
-                       it = monitor_lookup_.find(event->wd);
-                       if (it != monitor_lookup_.end())
-                       {
-                               log_error("3");
-                               std::string path = (*it).second;
-                               monitor_lookup_.erase(it);
-                               resource::reload(path);
-                       }
 
-                       byte += sizeof(*event) + event->len;
+/*] Resource loading
+ *************************************************************************/
 
-                       ++num_resources;
-               }
+typedef boost::weak_ptr<resource> resource_weakptr;
+static struct rsrc_list
+{
+       // this table holds weak references to any and all loaded resources
+       hash<std::string,resource_weakptr,hash_function> table;
+
+#ifdef DEBUG
+       // this destructor will let us know if the program exits while
+       // resources are still being held
+       ~rsrc_list()
+       {
+               hash<std::string,resource_weakptr,hash_function>::iterator it;
+               for (it = table.begin(); it != table.end(); ++it)
+                       log_warning("leaked resource:", (*it).first);
        }
 #endif
+} rsrc_list;
 
-       return num_resources;
-}
+#if ENABLE_HOTLOADING
+static struct watch_list
+{
+       // this table associates a watch descriptor with a loaded resource
+       hash<int,resource_weakptr,hash_function> table;
 
+       int fd;                         // the inotify file descriptor
 
-resource::~resource()
-{
-#if USE_HOTLOADING
-       inotify_rm_watch(monitor_fd_, wd_);
+       watch_list() :
+               fd(inotify_init1(IN_NONBLOCK)) {}
+
+       ~watch_list()
+       {
+               close(fd);
+       }
+
+       int add(resource_ptr rsrc)
+       {
+               int wd = inotify_add_watch(fd, rsrc->path().c_str(),
+                               IN_DELETE_SELF | IN_MODIFY);
+               table[wd] = rsrc;
+               return wd;
+       }
+       void remove(int wd)
+       {
+               if (wd != -1) inotify_rm_watch(fd, wd);
+       }
+} watch_list;
 #endif
-}
 
+resource_ptr resource::load(const std::string& name)
+{
+       std::string ext = stlplus::extension_part(name);
+       return load_with_path(find_file(name, ext), ext);
+}
 
-resource_ptr resource::load(const std::string& path)
+resource_ptr resource::load(const std::string& name, const std::string& ext)
 {
-       std::string extension = stlplus::extension_part(path);
+       return load_with_path(find_file(name, ext), ext);
+}
 
-       if (!find(path)) return resource_ptr();
+resource_ptr
+resource::load_with_path(const std::string& path, const std::string& ext)
+{
+       if (path.empty()) return resource_ptr();
 
+       // first try to lookup the resource
        hash<std::string,resource_weakptr,hash_function>::iterator it;
-       it = resource_table_.find(path);
-       if (it != resource_table_.end())
+       it = rsrc_list.table.find(path);
+       if (it != rsrc_list.table.end())
        {
-               resource_weakptr rsrc = (*it).second;
-               resource_ptr locked = rsrc.lock();
-               if (locked) return locked;
+               resource_ptr rsrc = (*it).second.lock();
+               if (rsrc) return rsrc;
        }
 
-       std::map<std::string,loader_ptr>::iterator jt;
-       jt = type_lookup_->find(extension);
-       if (jt != type_lookup_->end())
+       // then proceed to use the registered loader to get the resource
+       loader_ptr loader;
+       call_registry(ext, loader, lookup);
+       if (loader)
        {
-               resource_ptr rsrc((*jt).second->load(path));
-               rsrc->set_loader(path, (*jt).second);
-               resource_table_[path] = rsrc;
-
-#if USE_HOTLOADING
-               int wd = inotify_add_watch(monitor_fd_,
-                               path.c_str(), IN_MODIFY);
-               rsrc->set_watch_descriptor(wd);
-               monitor_lookup_[wd] = path;
+               resource_ptr rsrc(loader->load(path));
+               rsrc_list.table[path] = rsrc;
+               rsrc->path_ = path;
+               rsrc->type_ = ext;
+#if ENABLE_HOTLOADING
+               rsrc->wd_ = watch_list.add(rsrc);
 #endif
-
-               log_info("loaded", rsrc.get());
                return rsrc;
        }
 
+       log_warning("cannot load resource of unregistered type:", path);
        return resource_ptr();
 }
 
+/*] Hotloading
+ *************************************************************************/
 
-resource_ptr resource::reload(std::string& path)
+int resource::reload_as_needed()
 {
-       log_info("reloading...", path);
-       hash<std::string,resource_weakptr,hash_function>::iterator it;
-       it = resource_table_.find(path);
-       if (it != resource_table_.end())
+       int count = 0;
+
+#if ENABLE_HOTLOADING
+       char bytes[BUF_SIZE];
+       int num_bytes;
+       // an inotify file descriptor lets your read inotify_event structures
+       if (0 < (num_bytes = read(watch_list.fd, bytes, sizeof(bytes))))
        {
-               resource_weakptr rsrc = (*it).second;
-               resource_ptr locked = rsrc.lock();
-               if (locked)
+               char* end = bytes + num_bytes;
+               char* byte = bytes;
+
+               while (byte < end)
                {
-                       locked->reload();
-                       return locked;
+                       struct inotify_event* event = (struct inotify_event*)byte;
+                       byte += sizeof(*event) + event->len;
+
+                       hash<int,resource_weakptr,hash_function>::iterator it;
+                       it = watch_list.table.find(event->wd);
+                       if (it != watch_list.table.end())
+                       {
+                               resource_ptr rsrc = (*it).second.lock();
+                               if (rsrc)
+                               {
+                                       rsrc->reload();
+                                       ++count;
+
+                                       if (event->mask & IN_IGNORED)
+                                       {
+                                               watch_list.table.erase(event->wd);
+                                               rsrc->wd_ = watch_list.add(rsrc);
+                                       }
+                               }
+                               else
+                               {
+                                       // if we can't lock the resource, it was unloaded so we
+                                       // don't need to watch it anymore
+                                       watch_list.table.erase(event->wd);
+                               }
+                       }
                }
        }
-
-       return load(path);
+#endif
+       return count;
 }
 
 void resource::reload()
 {
-       log_info("reloaded", path_);
+       loader_ptr loader;
+       call_registry(type_, loader, lookup);
 
-       resource* resource = loader_->load(path_);
-       //*this = *resource;
+       resource_ptr resource(loader->load(path_));
        resource_ = resource->resource_;
        typeinfo_ = resource->typeinfo_;
        unloader_ = resource->unloader_;
-
-#if USE_HOTLOADING
-       int wd = inotify_add_watch(monitor_fd_,
-                       path_.c_str(), IN_MODIFY);
-       set_watch_descriptor(wd);
-       monitor_lookup_[wd] = path_;
-#endif
-
-       delete resource;
-}
-
-
-void resource::add_search_paths(const std::string& paths)
-{
-       std::vector<std::string> pathList;
-       boost::split(pathList, paths, boost::is_any_of(":"));
-
-       add_search_paths(pathList);
 }
 
-void resource::add_search_paths(const std::vector<std::string>& pathList)
+resource::~resource()
 {
-       std::vector<std::string>::const_iterator it;
-       for (it = pathList.begin(); it != pathList.end(); ++it)
-       {
-               std::string path(*it);
-
-               ASSERT(!path.empty() && "empty search path string");
-
-               // add a slash if there isn't one already
-               if (*path.rbegin() != '/') path += '/';
-
-#if defined(_WIN32)
-               //boost::replace_all(path, "/", "\\");
+       rsrc_list.table.erase(path_);
+#if ENABLE_HOTLOADING
+       watch_list.remove(wd_);
 #endif
-
-               search_paths_.push_back(path);
-               log_info << "added search path " << path << std::endl;
-       }
 }
 
+/*] Resource registration
+ *************************************************************************/
 
-bool resource::find(const std::string& path)
+bool resource::call_registry(const std::string& ext,
+               loader_ptr& loader, registry_action action)
 {
-       FILE* file = open_file(path);
-       if (file)
-       {
-               fclose(file);
-               return true;
-       }
+       static std::map<std::string,loader_ptr> table;
 
-       return false;
-}
-
-FILE* resource::open_file(const std::string& path, const std::string& mode)
-{
-#if defined(_WIN32)
-       // windows always has to be a little different
-       //boost::replace_all(path, "/", "\\");
-#endif
-
-       std::vector<std::string>::iterator it;
-       for (it = search_paths_.begin(); it != search_paths_.end(); ++it)
+       switch (action)
+       {
+       case lookup:
        {
-               // check path relative to search path
-               std::string complete_path(*it);
-               complete_path += path;
+               std::map<std::string,loader_ptr>::iterator it;
+               it = table.find(ext);
+               if (it != table.end()) loader = (*it).second;
+               break;
+       }
 
-               FILE* file = fopen(complete_path.c_str(), mode.c_str());
-               if (file) return file;
+       case set:
+               if (loader)
+                       table[ext] = loader;
+               else
+                       table.erase(ext);
+               break;
        }
 
-       // last ditch effort; maybe it's already a path to a valid resource
-       return fopen(path.c_str(), mode.c_str());
+       return loader;
 }
 
 
This page took 0.026743 seconds and 4 git commands to generate.