-/*] 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 <sys/inotify.h>
-#include <sys/ioctl.h>
#endif
#include <queue>
#include <boost/algorithm/string.hpp>
#include <boost/weak_ptr.hpp>
+
#include <stlplus/portability/file_system.hpp>
+#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 {
-
-bool resource::call_registry(const std::string& extension,
- loader_ptr& loader,
- registry_action action)
+
+/*] Filesystem searching
+ *************************************************************************/
+
+static std::vector<std::string> search_paths_;
+
+void resource::set_search_paths(const std::string& paths)
{
- static type_lookup table;
+ boost::split(search_paths_, paths, boost::is_any_of(":"));
+}
- switch (action)
+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<std::string>::iterator it;
+ for (it = search_paths_.begin(); it != search_paths_.end(); ++it)
{
- case set:
+ if (!prefix.empty())
{
- if (loader) table[extension] = loader;
- else table.erase(extension);
- break;
+ // 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;
}
- case lookup:
- {
- std::map<std::string,loader_ptr>::iterator it;
- it = table.find(extension);
- if (it != table.end()) loader = (*it).second;
- break;
- }
+ path = stlplus::filespec_to_path(*it, name);
+ log_debug("looking for", name, "at", path);
+ if (stlplus::file_exists(path)) return path;
}
- return loader;
+ log_error("cannot find resource file:", name);
+ return std::string();
}
-static std::string search_paths_;
-
-typedef boost::weak_ptr<resource> resource_weakptr;
-static hash<std::string,resource_weakptr,hash_function> resource_table_;
-
-static hash<std::string,std::string,hash_function> prefix_table_;
+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);
+}
-#ifdef USE_HOTLOADING
-static hash<int,std::string,hash_function> monitor_lookup_;
-static int monitor_fd_ = inotify_init1(IN_NONBLOCK);
-#endif
+/*] Resource loading
+ *************************************************************************/
-int resource::reload_as_needed()
+typedef boost::weak_ptr<resource> resource_weakptr;
+static struct rsrc_list
{
- int num_resources = 0;
+ // this table holds weak references to any and all loaded resources
+ hash<std::string,resource_weakptr,hash_function> 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<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;
- 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<int,resource_weakptr,hash_function> table;
- if (event->mask & IN_IGNORED)
- {
- log_warning("watch", event->wd, "removed");
- }
+ int fd; // the inotify file descriptor
- 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;
+ watch_list() :
+ fd(inotify_init1(IN_NONBLOCK)) {}
- ++num_resources;
- }
+ ~watch_list()
+ {
+ close(fd);
}
-#endif
-
- return num_resources;
-}
-
-resource::~resource()
-{
-#ifdef USE_HOTLOADING
- inotify_rm_watch(monitor_fd_, wd_);
+ 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,
- const std::string& ext)
+resource_ptr resource::load(const std::string& name)
{
- return load_with_path(find_file(name, ext));
+ std::string ext = stlplus::extension_part(name);
+ return load_with_path(find_file(name, ext), ext);
}
-resource_ptr resource::load(const std::string& name)
+resource_ptr resource::load(const std::string& name, const std::string& ext)
{
- return load_with_path(find_file(name));
+ return load_with_path(find_file(name, ext), ext);
}
-
-resource_ptr resource::load_with_path(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 (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;
}
+ // then proceed to use the registered loader to get the resource
loader_ptr loader;
- call_registry(extension, loader, lookup);
+ call_registry(ext, loader, lookup);
if (loader)
{
resource_ptr rsrc(loader->load(path));
- rsrc->set_loader(path, loader);
- 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;
+ 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 unknown type:", path);
+ 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_;
-
-#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
+ *************************************************************************/
-std::string resource::find_file(const std::string& name)
+bool resource::call_registry(const std::string& ext,
+ loader_ptr& loader, registry_action action)
{
- std::vector<std::string> paths;
- boost::split(paths, search_paths_, boost::is_any_of(":"));
-
- std::string ext = stlplus::extension_part(name);
- std::string prefix("hi");
+ static std::map<std::string,loader_ptr> table;
- loader_ptr loader;
- call_registry(ext, loader, lookup);
- if (loader) prefix = loader->prefix();
-
- log_info("find_file:", ext, prefix);
-
- std::vector<std::string>::iterator it;
- for (it = paths.begin(); it != paths.end(); ++it)
+ switch (action)
{
- std::string path = stlplus::create_filespec(*it, name);
- log_info("looking for", name, "at", path);
- if (stlplus::file_exists(path)) return path;
-
- // try it with the prefix added
- if (!prefix.empty())
- {
- *it = stlplus::create_filespec(*it, prefix);
- path = stlplus::create_filespec(*it, name);
- log_info("looking for", name, "at", path);
- if (stlplus::file_exists(path)) return path;
- }
+ case lookup:
+ {
+ std::map<std::string,loader_ptr>::iterator it;
+ it = table.find(ext);
+ if (it != table.end()) loader = (*it).second;
+ break;
}
-
- 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)
- {
- return find_file(stlplus::create_filename(name, ext));
+ case set:
+ if (loader)
+ table[ext] = loader;
+ else
+ table.erase(ext);
+ break;
}
- return find_file(name);
+
+ return loader;
}