initial runloop implementation
[chaz/yoink] / src / moof / resource.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include "config.h"
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
29 #ifndef BUF_SIZE
30 #define BUF_SIZE 4096
31 #endif
32
33
34 namespace moof {
35
36
37 /*] Filesystem searching
38 *************************************************************************/
39
40 static std::vector<std::string> search_paths_;
41
42 void resource::set_search_paths(const std::string& paths)
43 {
44 boost::split(search_paths_, paths, boost::is_any_of(":"));
45 }
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 resource::find_file(const std::string& name,
80 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 {
109 log_warning("leaked resource:", (*it).first);
110 }
111 }
112 #endif
113 } rsrc_list;
114
115 #if ENABLE_HOTLOADING
116 static struct watch_list
117 {
118 // this table associates a watch descriptor with a loaded resource
119 hash<int,resource_weakptr,hash_function> table;
120
121 int fd; // the inotify file descriptor
122
123 watch_list() :
124 fd(inotify_init1(IN_NONBLOCK)) {}
125
126 ~watch_list()
127 {
128 close(fd);
129 }
130
131 int add(resource_ptr rsrc)
132 {
133 int wd = inotify_add_watch(fd, rsrc->path().c_str(),
134 IN_DELETE_SELF | IN_MODIFY);
135 table[wd] = rsrc;
136 return wd;
137 }
138 void remove(int wd)
139 {
140 if (wd != -1) inotify_rm_watch(fd, wd);
141 }
142 } watch_list;
143 #endif
144
145
146 resource_ptr resource::load(const std::string& name)
147 {
148 std::string ext = stlplus::extension_part(name);
149 return load_with_path(find_file(name, ext), ext);
150 }
151
152 resource_ptr resource::load(const std::string& name,
153 const std::string& ext)
154 {
155 return load_with_path(find_file(name, ext), ext);
156 }
157
158
159 resource_ptr resource::load_with_path(const std::string& path,
160 const std::string& ext)
161 {
162 if (path.empty()) return resource_ptr();
163
164 // first try to lookup the resource
165 hash<std::string,resource_weakptr,hash_function>::iterator it;
166 it = rsrc_list.table.find(path);
167 if (it != rsrc_list.table.end())
168 {
169 resource_ptr rsrc = (*it).second.lock();
170 if (rsrc) return rsrc;
171 }
172
173 // then proceed to use the registered loader to get the resource
174 loader_ptr loader;
175 call_registry(ext, loader, lookup);
176 if (loader)
177 {
178 resource_ptr rsrc(loader->load(path));
179 rsrc_list.table[path] = rsrc;
180 rsrc->path_ = path;
181 rsrc->type_ = ext;
182 #if ENABLE_HOTLOADING
183 rsrc->wd_ = watch_list.add(rsrc);
184 #endif
185 return rsrc;
186 }
187
188 log_warning("cannot load resource of unregistered type:", path);
189 return resource_ptr();
190 }
191
192
193 /*] Hotloading
194 *************************************************************************/
195
196 int resource::reload_as_needed()
197 {
198 int count = 0;
199
200 #if ENABLE_HOTLOADING
201 char bytes[BUF_SIZE];
202 int num_bytes;
203 // an inotify file descriptor lets your read inotify_event structures
204 if (0 < (num_bytes = read(watch_list.fd, bytes, sizeof(bytes))))
205 {
206 char* end = bytes + num_bytes;
207 char* byte = bytes;
208
209 while (byte < end)
210 {
211 struct inotify_event* event = (struct inotify_event*)byte;
212 byte += sizeof(*event) + event->len;
213
214 hash<int,resource_weakptr,hash_function>::iterator it;
215 it = watch_list.table.find(event->wd);
216 if (it != watch_list.table.end())
217 {
218 resource_ptr rsrc = (*it).second.lock();
219 if (rsrc)
220 {
221 rsrc->reload();
222 ++count;
223
224 if (event->mask & IN_IGNORED)
225 {
226 watch_list.table.erase(event->wd);
227 rsrc->wd_ = watch_list.add(rsrc);
228 }
229 }
230 else
231 {
232 // if we can't lock the resource, it was unloaded so we
233 // don't need to watch it anymore
234 watch_list.table.erase(event->wd);
235 }
236 }
237 }
238 }
239 #endif
240
241 return count;
242 }
243
244 void resource::reload()
245 {
246 loader_ptr loader;
247 call_registry(type_, loader, lookup);
248
249 resource_ptr resource(loader->load(path_));
250 resource_ = resource->resource_;
251 typeinfo_ = resource->typeinfo_;
252 unloader_ = resource->unloader_;
253 }
254
255 resource::~resource()
256 {
257 rsrc_list.table.erase(path_);
258 #if ENABLE_HOTLOADING
259 watch_list.remove(wd_);
260 #endif
261 }
262
263
264 /*] Resource registration
265 *************************************************************************/
266
267 bool resource::call_registry(const std::string& ext,
268 loader_ptr& loader,
269 registry_action action)
270 {
271 static std::map<std::string,loader_ptr> table;
272
273 switch (action)
274 {
275 case set:
276 {
277 if (loader) table[ext] = loader;
278 else table.erase(ext);
279 break;
280 }
281
282 case lookup:
283 {
284 std::map<std::string,loader_ptr>::iterator it;
285 it = table.find(ext);
286 if (it != table.end()) loader = (*it).second;
287 break;
288 }
289 }
290
291 return loader;
292 }
293
294
295 } // namespace moof
296
This page took 0.042627 seconds and 4 git commands to generate.