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