X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fresource.cc;h=8188313b8442011b615925cd61e1aafcf6b81260;hp=314f80b5db81707e73b4cb3d91300f6d5b5a8ea0;hb=HEAD;hpb=1da520638918096276158ecdfaeebc14a3d70be7 diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 314f80b..8188313 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -1,232 +1,285 @@ -/*] 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" - -#ifdef USE_HOTLOADING -#include -#include #endif #include #include #include + #include +#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 { - -void resource::print_types() + + +/*] Filesystem searching + *************************************************************************/ + +static std::vector search_paths_; + +void resource::set_search_paths(const std::string& paths) { + boost::split(search_paths_, paths, boost::is_any_of(":")); } -void resource::manage_loader(const std::string& extension, loader_ptr& loader, bool set) +std::string resource::find_file(const std::string& name) { - static type_lookup lookup; + std::string ext = stlplus::extension_part(name); + std::string prefix; + std::string path; + + loader_ptr loader; + call_registry(ext, loader, lookup); + if (loader) prefix = loader->prefix(); - if (loader || set) + std::vector::iterator it; + for (it = search_paths_.begin(); it != search_paths_.end(); ++it) { - lookup[extension] = loader; + 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; + } + + path = stlplus::filespec_to_path(*it, name); + log_debug("looking for", name, "at", path); + if (stlplus::file_exists(path)) return path; } - else + + log_error("cannot find resource file:", name); + return std::string(); +} + +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) { - std::map::iterator it; - it = lookup.find(extension); - if (it != lookup.end()) loader = (*it).second; + // try the explicit extension first + return find_file(stlplus::create_filename(name, ext)); } + return find_file(name); } -static std::string search_paths_; - -typedef boost::weak_ptr resource_weakptr; -static hash resource_table_; - -// static member -//resource::type_lookup_ptr resource::type_lookup_; -//resource::type_lookup resource::type_lookup_; +/*] Resource loading + *************************************************************************/ -#ifdef USE_HOTLOADING -static hash monitor_lookup_; -static int monitor_fd_ = inotify_init1(IN_NONBLOCK); -#endif - -int resource::reload_as_needed() +typedef boost::weak_ptr resource_weakptr; +static struct rsrc_list { - int num_resources = 0; + // this table holds weak references to any and all loaded resources + hash table; -#ifdef USE_HOTLOADING - log_info("hotloading?"); - char bytes[BUF_SIZE]; - int num_bytes; - if (0 < (num_bytes = read(monitor_fd_, bytes, num_bytes))) +#ifdef DEBUG + // this destructor will let us know if the program exits while + // resources are still being held + ~rsrc_list() { - char* end = bytes + num_bytes; - char* byte = bytes; - - log_warning("num_bytes:", num_bytes); - log_error("1"); + hash::iterator it; + for (it = table.begin(); it != table.end(); ++it) + log_warning("leaked resource:", (*it).first); + } +#endif +} rsrc_list; - while (byte < end) - { - struct inotify_event* event = (struct inotify_event*)byte; +#if ENABLE_HOTLOADING +static struct watch_list +{ + // this table associates a watch descriptor with a loaded resource + hash table; - if (event->mask & IN_IGNORED) - { - log_warning("watch", event->wd, "removed"); - } + int fd; // the inotify file descriptor - 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); - } + watch_list() : + fd(inotify_init1(IN_NONBLOCK)) {} - byte += sizeof(*event) + event->len; + ~watch_list() + { + close(fd); + } - ++num_resources; - } + 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 - return num_resources; +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::~resource() +resource_ptr resource::load(const std::string& name, const std::string& ext) { -#ifdef USE_HOTLOADING - inotify_rm_watch(monitor_fd_, wd_); -#endif + return load_with_path(find_file(name, ext), ext); } - -resource_ptr resource::load(const std::string& path) +resource_ptr +resource::load_with_path(const std::string& path, const std::string& ext) { - std::string extension = stlplus::extension_part(path); - - std::string path1 = find_file(path); - if (!find(path1)) - { - log_error("trying to load missing resource:", path1); - return resource_ptr(); - } + if (path.empty()) return resource_ptr(); + // first try to lookup the resource hash::iterator it; - it = resource_table_.find(path1); - 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; } + // then proceed to use the registered loader to get the resource loader_ptr loader; - manage_loader(extension, loader); + call_registry(ext, loader, lookup); if (loader) { - resource_ptr rsrc(loader->load(path1)); - rsrc->set_loader(path1, loader); - resource_table_[path1] = rsrc; - -#ifdef USE_HOTLOADING - int wd = inotify_add_watch(monitor_fd_, path1.c_str(), IN_MODIFY); - rsrc->set_watch_descriptor(wd); - monitor_lookup_[wd] = path1; + 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) +resource::~resource() { - search_paths_ = paths; + rsrc_list.table.erase(path_); +#if ENABLE_HOTLOADING + watch_list.remove(wd_); +#endif } +/*] Resource registration + *************************************************************************/ -bool resource::find(const std::string& path) +bool resource::call_registry(const std::string& ext, + loader_ptr& loader, registry_action action) { - std::string file = stlplus::lookup(path, search_paths_, ":"); - log_info("found file", file, "in", search_paths_); - return !stlplus::lookup(path, search_paths_, ":").empty(); -} + static std::map table; -std::string resource::find_file(const std::string& name) -{ - log_info("looking for", name, "in", search_paths_); - return stlplus::lookup(name, search_paths_, ":"); -} + switch (action) + { + case lookup: + { + std::map::iterator it; + it = table.find(ext); + if (it != table.end()) loader = (*it).second; + break; + } -FILE* resource::open_file(const std::string& path, const std::string& mode) -{ - std::string file = stlplus::lookup(path, search_paths_, ":"); - return fopen(file.c_str(), mode.c_str()); + case set: + if (loader) + table[ext] = loader; + else + table.erase(ext); + break; + } + + return loader; }