X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fresource.cc;h=8188313b8442011b615925cd61e1aafcf6b81260;hp=5cd75360d85d5c8c5041d76da80987bc830cccf0;hb=HEAD;hpb=e0fb747f2e647115d0b8953615c254d25c045345 diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 5cd7536..8188313 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -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 @@ -16,235 +18,268 @@ #include -#include "hash.hh" -#include "resource.hh" - -#if HAVE_CONFIG_H -#include "../config.h" -#endif - -#ifdef USE_HOTLOADING +#if ENABLE_HOTLOADING #include #include #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 search_paths_; -typedef boost::weak_ptr resource_weakptr; -static hash resource_table_; -// static member -resource::type_lookup_ptr resource::type_lookup_; +/*] Filesystem searching + *************************************************************************/ +static std::vector search_paths_; -#ifdef USE_HOTLOADING -static hash 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; -#ifdef 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::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::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_weakptr; +static struct rsrc_list +{ + // this table holds weak references to any and all loaded resources + hash table; + +#ifdef DEBUG + // this destructor will let us know if the program exits while + // resources are still being held + ~rsrc_list() + { + hash::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 table; + int fd; // the inotify file descriptor -resource::~resource() -{ -#ifdef 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::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::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; - -#ifdef 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::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::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_; - -#ifdef 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 pathList; - boost::split(pathList, paths, boost::is_any_of(":")); - - add_search_paths(pathList); } -void resource::add_search_paths(const std::vector& pathList) +resource::~resource() { - std::vector::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 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::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::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; }