X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fresource.cc;h=35cd663ded7decff86f96321916b8ab5cef71f5b;hp=5cd75360d85d5c8c5041d76da80987bc830cccf0;hb=e9a16d767785b1a903a4466ff26265a5f7dae9e5;hpb=e0fb747f2e647115d0b8953615c254d25c045345 diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 5cd7536..35cd663 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -9,24 +9,22 @@ * **************************************************************************/ +#include "config.h" + #include +#ifdef USE_HOTLOADING +#include +#include +#endif + #include #include - #include #include "hash.hh" #include "resource.hh" -#if HAVE_CONFIG_H -#include "../config.h" -#endif - -#ifdef USE_HOTLOADING -#include -#include -#endif #ifndef BUF_SIZE #define BUF_SIZE 4096 @@ -36,215 +34,261 @@ namespace moof { +/*] Filesystem searching + *************************************************************************/ + static std::vector search_paths_; -typedef boost::weak_ptr resource_weakptr; -static hash resource_table_; +void resource::set_search_paths(const std::string& paths) +{ + boost::split(search_paths_, paths, boost::is_any_of(":")); +} -// static member -resource::type_lookup_ptr resource::type_lookup_; +std::string resource::find_file(const std::string& name) +{ + std::string ext = stlplus::extension_part(name); + std::string prefix; + std::string path; -#ifdef USE_HOTLOADING -static hash monitor_lookup_; -static int monitor_fd_ = inotify_init1(IN_NONBLOCK); -#endif + loader_ptr loader; + call_registry(ext, loader, lookup); + if (loader) prefix = loader->prefix(); -int resource::reload_as_needed() -{ - int num_resources = 0; + std::vector::iterator it; + for (it = search_paths_.begin(); it != search_paths_.end(); ++it) + { + 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; + } -#ifdef USE_HOTLOADING - log_info("hotloading?"); - char bytes[BUF_SIZE]; - int num_bytes; - if (0 < (num_bytes = read(monitor_fd_, bytes, num_bytes))) + path = stlplus::filespec_to_path(*it, name); + log_debug("looking for", name, "at", path); + if (stlplus::file_exists(path)) return path; + } + + 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) { - char* end = bytes + num_bytes; - char* byte = bytes; + // try the explicit extension first + return find_file(stlplus::create_filename(name, ext)); + } + return find_file(name); +} - log_warning("num_bytes:", num_bytes); - log_error("1"); - while (byte < end) +/*] Resource loading + *************************************************************************/ + +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) { - struct inotify_event* event = (struct inotify_event*)byte; + log_warning("leaked resource:", (*it).first); + } + } +#endif +} rsrc_list; - if (event->mask & IN_IGNORED) - { - log_warning("watch", event->wd, "removed"); - } +#ifdef USE_HOTLOADING +static struct watch_list +{ + // this table associates a watch descriptor with a loaded resource + hash table; - 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); - } + int fd; // the inotify file descriptor - byte += sizeof(*event) + event->len; + watch_list() : + fd(inotify_init1(IN_NONBLOCK)) {} - ++num_resources; - } + ~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 - 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); - - if (!find(path)) return resource_ptr(); + 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; - + resource_ptr rsrc(loader->load(path)); + rsrc_list.table[path] = rsrc; + rsrc->path_ = path; + rsrc->type_ = ext; #ifdef USE_HOTLOADING - int wd = inotify_add_watch(monitor_fd_, - path.c_str(), IN_MODIFY); - rsrc->set_watch_descriptor(wd); - monitor_lookup_[wd] = path; + 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(); } -resource_ptr resource::reload(std::string& path) +/*] Hotloading + *************************************************************************/ + +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; + +#ifdef USE_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); + } + } } } +#endif - return load(path); + 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_); +#ifdef USE_HOTLOADING + watch_list.remove(wd_); #endif - - search_paths_.push_back(path); - log_info << "added search path " << path << std::endl; - } } -bool resource::find(const std::string& path) -{ - FILE* file = open_file(path); - if (file) - { - fclose(file); - return true; - } - - return false; -} +/*] Resource registration + *************************************************************************/ -FILE* resource::open_file(const std::string& path, const std::string& mode) +bool resource::call_registry(const std::string& ext, + loader_ptr& loader, + registry_action action) { -#if defined(_WIN32) - // windows always has to be a little different - //boost::replace_all(path, "/", "\\"); -#endif + static std::map table; - std::vector::iterator it; - for (it = search_paths_.begin(); it != search_paths_.end(); ++it) + switch (action) { - // check path relative to search path - std::string complete_path(*it); - complete_path += path; + case set: + { + if (loader) table[ext] = loader; + else table.erase(ext); + break; + } - FILE* file = fopen(complete_path.c_str(), mode.c_str()); - if (file) return file; + case lookup: + { + std::map::iterator it; + it = table.find(ext); + if (it != table.end()) loader = (*it).second; + break; + } } - // last ditch effort; maybe it's already a path to a valid resource - return fopen(path.c_str(), mode.c_str()); + return loader; }