]> Dogcows Code - chaz/yoink/blob - src/moof/resource.cc
fixed documentation about where to find licenses
[chaz/yoink] / src / moof / resource.cc
1
2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
4 *
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
7 *
8 *****************************************************************************/
9
10 #if HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #include <queue>
15
16 #include <boost/algorithm/string.hpp>
17 #include <boost/weak_ptr.hpp>
18
19 #include <stlplus/portability/file_system.hpp>
20
21 #if ENABLE_HOTLOADING
22 #include <sys/inotify.h>
23 #include <sys/ioctl.h>
24 #endif
25
26 #include "hash.hh"
27 #include "log.hh"
28 #include "resource.hh"
29
30 #ifndef BUF_SIZE
31 #define BUF_SIZE 8192
32 #endif
33
34
35 namespace moof {
36
37
38 /*] Filesystem searching
39 *************************************************************************/
40
41 static std::vector<std::string> search_paths_;
42
43 void resource::set_search_paths(const std::string& paths)
44 {
45 boost::split(search_paths_, paths, boost::is_any_of(":"));
46 }
47
48 std::string resource::find_file(const std::string& name)
49 {
50 std::string ext = stlplus::extension_part(name);
51 std::string prefix;
52 std::string path;
53
54 loader_ptr loader;
55 call_registry(ext, loader, lookup);
56 if (loader) prefix = loader->prefix();
57
58 std::vector<std::string>::iterator it;
59 for (it = search_paths_.begin(); it != search_paths_.end(); ++it)
60 {
61 if (!prefix.empty())
62 {
63 // try it with the prefix first
64 path = stlplus::filespec_to_path(*it, prefix);
65 path = stlplus::filespec_to_path(path, name);
66 log_debug("looking for", name, "at", path);
67 if (stlplus::file_exists(path)) return path;
68 }
69
70 path = stlplus::filespec_to_path(*it, name);
71 log_debug("looking for", name, "at", path);
72 if (stlplus::file_exists(path)) return path;
73 }
74
75 log_error("cannot find resource file:", name);
76 return std::string();
77 }
78
79 std::string
80 resource::find_file(const std::string& name, const std::string& ext)
81 {
82 std::string actual_ext = stlplus::extension_part(name);
83 if (actual_ext != ext)
84 {
85 // try the explicit extension first
86 return find_file(stlplus::create_filename(name, ext));
87 }
88 return find_file(name);
89 }
90
91
92 /*] Resource loading
93 *************************************************************************/
94
95 typedef boost::weak_ptr<resource> resource_weakptr;
96 static struct rsrc_list
97 {
98 // this table holds weak references to any and all loaded resources
99 hash<std::string,resource_weakptr,hash_function> table;
100
101 #ifdef DEBUG
102 // this destructor will let us know if the program exits while
103 // resources are still being held
104 ~rsrc_list()
105 {
106 hash<std::string,resource_weakptr,hash_function>::iterator it;
107 for (it = table.begin(); it != table.end(); ++it)
108 log_warning("leaked resource:", (*it).first);
109 }
110 #endif
111 } rsrc_list;
112
113 #if ENABLE_HOTLOADING
114 static struct watch_list
115 {
116 // this table associates a watch descriptor with a loaded resource
117 hash<int,resource_weakptr,hash_function> table;
118
119 int fd; // the inotify file descriptor
120
121 watch_list() :
122 fd(inotify_init1(IN_NONBLOCK)) {}
123
124 ~watch_list()
125 {
126 close(fd);
127 }
128
129 int add(resource_ptr rsrc)
130 {
131 int wd = inotify_add_watch(fd, rsrc->path().c_str(),
132 IN_DELETE_SELF | IN_MODIFY);
133 table[wd] = rsrc;
134 return wd;
135 }
136 void remove(int wd)
137 {
138 if (wd != -1) inotify_rm_watch(fd, wd);
139 }
140 } watch_list;
141 #endif
142
143 resource_ptr resource::load(const std::string& name)
144 {
145 std::string ext = stlplus::extension_part(name);
146 return load_with_path(find_file(name, ext), ext);
147 }
148
149 resource_ptr resource::load(const std::string& name, const std::string& ext)
150 {
151 return load_with_path(find_file(name, ext), ext);
152 }
153
154 resource_ptr
155 resource::load_with_path(const std::string& path, const std::string& ext)
156 {
157 if (path.empty()) return resource_ptr();
158
159 // first try to lookup the resource
160 hash<std::string,resource_weakptr,hash_function>::iterator it;
161 it = rsrc_list.table.find(path);
162 if (it != rsrc_list.table.end())
163 {
164 resource_ptr rsrc = (*it).second.lock();
165 if (rsrc) return rsrc;
166 }
167
168 // then proceed to use the registered loader to get the resource
169 loader_ptr loader;
170 call_registry(ext, loader, lookup);
171 if (loader)
172 {
173 resource_ptr rsrc(loader->load(path));
174 rsrc_list.table[path] = rsrc;
175 rsrc->path_ = path;
176 rsrc->type_ = ext;
177 #if ENABLE_HOTLOADING
178 rsrc->wd_ = watch_list.add(rsrc);
179 #endif
180 return rsrc;
181 }
182
183 log_warning("cannot load resource of unregistered type:", path);
184 return resource_ptr();
185 }
186
187 /*] Hotloading
188 *************************************************************************/
189
190 int resource::reload_as_needed()
191 {
192 int count = 0;
193
194 #if ENABLE_HOTLOADING
195 char bytes[BUF_SIZE];
196 int num_bytes;
197 // an inotify file descriptor lets your read inotify_event structures
198 if (0 < (num_bytes = read(watch_list.fd, bytes, sizeof(bytes))))
199 {
200 char* end = bytes + num_bytes;
201 char* byte = bytes;
202
203 while (byte < end)
204 {
205 struct inotify_event* event = (struct inotify_event*)byte;
206 byte += sizeof(*event) + event->len;
207
208 hash<int,resource_weakptr,hash_function>::iterator it;
209 it = watch_list.table.find(event->wd);
210 if (it != watch_list.table.end())
211 {
212 resource_ptr rsrc = (*it).second.lock();
213 if (rsrc)
214 {
215 rsrc->reload();
216 ++count;
217
218 if (event->mask & IN_IGNORED)
219 {
220 watch_list.table.erase(event->wd);
221 rsrc->wd_ = watch_list.add(rsrc);
222 }
223 }
224 else
225 {
226 // if we can't lock the resource, it was unloaded so we
227 // don't need to watch it anymore
228 watch_list.table.erase(event->wd);
229 }
230 }
231 }
232 }
233 #endif
234 return count;
235 }
236
237 void resource::reload()
238 {
239 loader_ptr loader;
240 call_registry(type_, loader, lookup);
241
242 resource_ptr resource(loader->load(path_));
243 resource_ = resource->resource_;
244 typeinfo_ = resource->typeinfo_;
245 unloader_ = resource->unloader_;
246 }
247
248 resource::~resource()
249 {
250 rsrc_list.table.erase(path_);
251 #if ENABLE_HOTLOADING
252 watch_list.remove(wd_);
253 #endif
254 }
255
256 /*] Resource registration
257 *************************************************************************/
258
259 bool resource::call_registry(const std::string& ext,
260 loader_ptr& loader, registry_action action)
261 {
262 static std::map<std::string,loader_ptr> table;
263
264 switch (action)
265 {
266 case lookup:
267 {
268 std::map<std::string,loader_ptr>::iterator it;
269 it = table.find(ext);
270 if (it != table.end()) loader = (*it).second;
271 break;
272 }
273
274 case set:
275 if (loader)
276 table[ext] = loader;
277 else
278 table.erase(ext);
279 break;
280 }
281
282 return loader;
283 }
284
285
286 } // namespace moof
287
This page took 0.042615 seconds and 4 git commands to generate.