X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fresource.cc;h=8188313b8442011b615925cd61e1aafcf6b81260;hp=3512878bb039f5cede9b164940c744eccadd664f;hb=HEAD;hpb=831f04d4bc19a390415ac0bbac4331c7a65509bc diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 3512878..8188313 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -1,129 +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" +#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 8192 +#endif + namespace moof { -// static member -std::vector resource::search_paths_; +/*] Filesystem searching + *************************************************************************/ +static std::vector search_paths_; -void resource::add_search_paths(const std::string& paths) +void resource::set_search_paths(const std::string& paths) { - std::vector pathList; - boost::split(pathList, paths, boost::is_any_of(":")); + boost::split(search_paths_, paths, boost::is_any_of(":")); +} + +std::string resource::find_file(const std::string& name) +{ + 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(); + + 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; + } - add_search_paths(pathList); + 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(); } -void resource::add_search_paths(const std::vector& pathList) +std::string +resource::find_file(const std::string& name, const std::string& ext) { - std::vector::const_iterator it; - for (it = pathList.begin(); it != pathList.end(); ++it) + std::string actual_ext = stlplus::extension_part(name); + if (actual_ext != ext) { - std::string path(*it); + // try the explicit extension first + return find_file(stlplus::create_filename(name, ext)); + } + return find_file(name); +} + - ASSERT(!path.empty() && "empty search path string"); +/*] Resource loading + *************************************************************************/ - // add a slash if there isn't one already - if (*path.rbegin() != '/') path += '/'; +typedef boost::weak_ptr resource_weakptr; +static struct rsrc_list +{ + // this table holds weak references to any and all loaded resources + hash table; -#if defined(_WIN32) - boost::replace_all(path, "/", "\\"); +#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; + +#if ENABLE_HOTLOADING +static struct watch_list +{ + // this table associates a watch descriptor with a loaded resource + hash table; - search_paths_.push_back(path); - log_info << "added search path " << path << std::endl; + int fd; // the inotify file descriptor + + 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& name, const std::string& ext) +{ + return load_with_path(find_file(name, ext), ext); +} -bool resource::find_path(std::string& path, - const std::string& prefix, - const std::string& extension) +resource_ptr +resource::load_with_path(const std::string& path, const std::string& ext) { - FILE* file = open_file(path, prefix, extension); - if (file) + if (path.empty()) return resource_ptr(); + + // first try to lookup the resource + hash::iterator it; + it = rsrc_list.table.find(path); + if (it != rsrc_list.table.end()) { - fclose(file); - return true; + resource_ptr rsrc = (*it).second.lock(); + if (rsrc) return rsrc; } - return false; + // then proceed to use the registered loader to get the resource + loader_ptr loader; + call_registry(ext, loader, lookup); + if (loader) + { + 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 + return rsrc; + } + + log_warning("cannot load resource of unregistered type:", path); + return resource_ptr(); } -FILE* resource::open_file(std::string& path, - const std::string& prefix, - const std::string& extension, - const std::string& mode) -{ -#if defined(_WIN32) - // windows always has to be a little different - boost::replace_all(path, "/", "\\"); - std::string temp_prefix(prefix); - boost::replace_all(temp_prefix, "/", "\\"); - std::vector preList; - boost::split(preList, temp_prefix, boost::is_any_of(":")); -#else - std::vector preList; - boost::split(preList, prefix, boost::is_any_of(":")); -#endif +/*] Hotloading + *************************************************************************/ - std::vector postList; - boost::split(postList, extension, boost::is_any_of(":")); +int resource::reload_as_needed() +{ + int count = 0; - std::vector::iterator it; - for (it = search_paths_.begin(); it != search_paths_.end(); ++it) +#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)))) { - std::vector::iterator jt; - for (jt = preList.begin(); jt != preList.end(); ++jt) + char* end = bytes + num_bytes; + char* byte = bytes; + + while (byte < end) { - std::vector::iterator kt; - for (kt = postList.begin(); kt != postList.end(); ++kt) + 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()) { - std::string realPath(*it); - realPath += *jt; - realPath += path; - realPath += "."; - realPath += *kt; - - FILE* file = fopen(realPath.c_str(), mode.c_str()); - if (file) + 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 { - path = realPath; - return file; + // 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 count; +} - // check path relative to search path - std::string realPath(*it); - realPath += path; +void resource::reload() +{ + loader_ptr loader; + call_registry(type_, loader, lookup); - FILE* file = fopen(realPath.c_str(), mode.c_str()); - if (file) - { - path = realPath; - return file; - } + resource_ptr resource(loader->load(path_)); + resource_ = resource->resource_; + typeinfo_ = resource->typeinfo_; + unloader_ = resource->unloader_; +} + +resource::~resource() +{ + rsrc_list.table.erase(path_); +#if ENABLE_HOTLOADING + watch_list.remove(wd_); +#endif +} + +/*] Resource registration + *************************************************************************/ + +bool resource::call_registry(const std::string& ext, + loader_ptr& loader, registry_action action) +{ + static std::map table; + + switch (action) + { + case lookup: + { + std::map::iterator it; + it = table.find(ext); + if (it != table.end()) loader = (*it).second; + break; + } + + 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; }