rev |
line source |
nuclear@5
|
1 #include <stdio.h>
|
nuclear@5
|
2 #include <stdlib.h>
|
nuclear@5
|
3 #include <string.h>
|
nuclear@5
|
4 #include <assert.h>
|
nuclear@10
|
5 #include <pthread.h>
|
nuclear@5
|
6 #include "resman.h"
|
nuclear@22
|
7 #include "resman_impl.h"
|
nuclear@5
|
8 #include "dynarr.h"
|
nuclear@22
|
9 #include "filewatch.h"
|
nuclear@5
|
10
|
nuclear@5
|
11
|
nuclear@5
|
12 static int find_resource(struct resman *rman, const char *fname);
|
nuclear@5
|
13 static int add_resource(struct resman *rman, const char *fname, void *data);
|
nuclear@13
|
14 static void remove_resource(struct resman *rman, int idx);
|
nuclear@10
|
15 static void work_func(void *data, void *cls);
|
nuclear@5
|
16
|
nuclear@21
|
17
|
nuclear@5
|
18 struct resman *resman_create(void)
|
nuclear@5
|
19 {
|
nuclear@5
|
20 struct resman *rman = malloc(sizeof *rman);
|
nuclear@5
|
21 if(resman_init(rman) == -1) {
|
nuclear@5
|
22 free(rman);
|
nuclear@5
|
23 return 0;
|
nuclear@5
|
24 }
|
nuclear@5
|
25 return rman;
|
nuclear@5
|
26 }
|
nuclear@5
|
27
|
nuclear@5
|
28 void resman_free(struct resman *rman)
|
nuclear@5
|
29 {
|
nuclear@5
|
30 resman_destroy(rman);
|
nuclear@5
|
31 free(rman);
|
nuclear@5
|
32 }
|
nuclear@5
|
33
|
nuclear@5
|
34 int resman_init(struct resman *rman)
|
nuclear@5
|
35 {
|
nuclear@14
|
36 const char *env;
|
nuclear@14
|
37 int num_threads = TPOOL_AUTO;
|
nuclear@14
|
38
|
nuclear@5
|
39 memset(rman, 0, sizeof *rman);
|
nuclear@5
|
40
|
nuclear@14
|
41 if((env = getenv("RESMAN_THREADS"))) {
|
nuclear@14
|
42 num_threads = atoi(env);
|
nuclear@14
|
43 }
|
nuclear@14
|
44
|
nuclear@22
|
45 if(resman_init_file_monitor(rman) == -1) {
|
nuclear@21
|
46 return -1;
|
nuclear@21
|
47 }
|
nuclear@21
|
48
|
nuclear@14
|
49 if(!(rman->tpool = tpool_create(num_threads))) {
|
nuclear@10
|
50 return -1;
|
nuclear@10
|
51 }
|
nuclear@10
|
52 tpool_set_work_func(rman->tpool, work_func, rman);
|
nuclear@10
|
53
|
nuclear@5
|
54 if(!(rman->res = dynarr_alloc(0, sizeof *rman->res))) {
|
nuclear@10
|
55 tpool_free(rman->tpool);
|
nuclear@5
|
56 return -1;
|
nuclear@5
|
57 }
|
nuclear@5
|
58
|
nuclear@13
|
59 pthread_mutex_init(&rman->lock, 0);
|
nuclear@13
|
60
|
nuclear@5
|
61 return 0;
|
nuclear@5
|
62 }
|
nuclear@5
|
63
|
nuclear@5
|
64 void resman_destroy(struct resman *rman)
|
nuclear@5
|
65 {
|
nuclear@5
|
66 int i;
|
nuclear@5
|
67 if(!rman) return;
|
nuclear@5
|
68
|
nuclear@5
|
69 for(i=0; i<dynarr_size(rman->res); i++) {
|
nuclear@5
|
70 if(rman->destroy_func) {
|
nuclear@11
|
71 rman->destroy_func(i, rman->destroy_func_cls);
|
nuclear@5
|
72 }
|
nuclear@11
|
73 free(rman->res[i]);
|
nuclear@5
|
74 }
|
nuclear@5
|
75 dynarr_free(rman->res);
|
nuclear@10
|
76
|
nuclear@10
|
77 tpool_free(rman->tpool);
|
nuclear@13
|
78
|
nuclear@22
|
79 resman_destroy_file_monitor(rman);
|
nuclear@21
|
80
|
nuclear@13
|
81 pthread_mutex_destroy(&rman->lock);
|
nuclear@5
|
82 }
|
nuclear@5
|
83
|
nuclear@5
|
84
|
nuclear@5
|
85 void resman_set_load_func(struct resman *rman, resman_load_func func, void *cls)
|
nuclear@5
|
86 {
|
nuclear@5
|
87 rman->load_func = func;
|
nuclear@5
|
88 rman->load_func_cls = cls;
|
nuclear@5
|
89 }
|
nuclear@5
|
90
|
nuclear@10
|
91 void resman_set_done_func(struct resman *rman, resman_done_func func, void *cls)
|
nuclear@5
|
92 {
|
nuclear@10
|
93 rman->done_func = func;
|
nuclear@10
|
94 rman->done_func_cls = cls;
|
nuclear@5
|
95 }
|
nuclear@5
|
96
|
nuclear@5
|
97 void resman_set_destroy_func(struct resman *rman, resman_destroy_func func, void *cls)
|
nuclear@5
|
98 {
|
nuclear@5
|
99 rman->destroy_func = func;
|
nuclear@5
|
100 rman->destroy_func_cls = cls;
|
nuclear@5
|
101 }
|
nuclear@5
|
102
|
nuclear@5
|
103 int resman_lookup(struct resman *rman, const char *fname, void *data)
|
nuclear@5
|
104 {
|
nuclear@5
|
105 int ridx;
|
nuclear@5
|
106
|
nuclear@5
|
107 if((ridx = find_resource(rman, fname)) != -1) {
|
nuclear@5
|
108 return ridx;
|
nuclear@5
|
109 }
|
nuclear@5
|
110
|
nuclear@5
|
111 /* resource not found, create a new one and start a loading job */
|
nuclear@5
|
112 return add_resource(rman, fname, data);
|
nuclear@5
|
113 }
|
nuclear@5
|
114
|
nuclear@5
|
115 void resman_wait(struct resman *rman, int id)
|
nuclear@5
|
116 {
|
nuclear@5
|
117 /* TODO */
|
nuclear@5
|
118 }
|
nuclear@5
|
119
|
nuclear@5
|
120 int resman_poll(struct resman *rman)
|
nuclear@5
|
121 {
|
nuclear@10
|
122 int i, num_res;
|
nuclear@10
|
123
|
nuclear@14
|
124 /* first check all the resources to see if any is pending deletion */
|
nuclear@14
|
125 num_res = dynarr_size(rman->res);
|
nuclear@14
|
126 for(i=0; i<num_res; i++) {
|
nuclear@14
|
127 struct resource *res = rman->res[i];
|
nuclear@14
|
128 if(!res) {
|
nuclear@14
|
129 continue;
|
nuclear@14
|
130 }
|
nuclear@14
|
131
|
nuclear@14
|
132 if(res->delete_pending) {
|
nuclear@14
|
133 if(rman->destroy_func) {
|
nuclear@14
|
134 rman->destroy_func(i, rman->destroy_func_cls);
|
nuclear@14
|
135 }
|
nuclear@14
|
136 remove_resource(rman, i);
|
nuclear@14
|
137 }
|
nuclear@14
|
138 }
|
nuclear@14
|
139
|
nuclear@14
|
140
|
nuclear@21
|
141 /* then check for modified files */
|
nuclear@22
|
142 resman_check_watch(rman);
|
nuclear@21
|
143
|
nuclear@21
|
144
|
nuclear@10
|
145 if(!rman->done_func) {
|
nuclear@10
|
146 return 0; /* no done callback; there's no point in checking anything */
|
nuclear@10
|
147 }
|
nuclear@10
|
148
|
nuclear@10
|
149 for(i=0; i<num_res; i++) {
|
nuclear@11
|
150 struct resource *res = rman->res[i];
|
nuclear@13
|
151 if(!res) {
|
nuclear@13
|
152 continue;
|
nuclear@13
|
153 }
|
nuclear@10
|
154
|
nuclear@21
|
155 pthread_mutex_lock(&res->lock);
|
nuclear@10
|
156 if(!res->done_pending) {
|
nuclear@21
|
157 pthread_mutex_unlock(&res->lock);
|
nuclear@10
|
158 continue;
|
nuclear@10
|
159 }
|
nuclear@10
|
160
|
nuclear@10
|
161 /* so a done callback *is* pending... */
|
nuclear@10
|
162 res->done_pending = 0;
|
nuclear@13
|
163 if(rman->done_func(i, rman->done_func_cls) == -1) {
|
nuclear@21
|
164 /* done-func returned -1, so let's remove the resource
|
nuclear@21
|
165 * but only if this was the first load. Otherwise keep it
|
nuclear@21
|
166 * around in case it gets valid again...
|
nuclear@21
|
167 */
|
nuclear@21
|
168 if(res->num_loads == 0) {
|
nuclear@21
|
169 pthread_mutex_unlock(&res->lock);
|
nuclear@21
|
170 remove_resource(rman, i);
|
nuclear@21
|
171 continue;
|
nuclear@21
|
172 }
|
nuclear@13
|
173 }
|
nuclear@21
|
174 res->num_loads++;
|
nuclear@21
|
175
|
nuclear@22
|
176 resman_start_watch(rman, res); /* start watching the file for modifications */
|
nuclear@21
|
177 pthread_mutex_unlock(&res->lock);
|
nuclear@10
|
178 }
|
nuclear@5
|
179 return 0;
|
nuclear@5
|
180 }
|
nuclear@5
|
181
|
nuclear@11
|
182 const char *resman_get_res_name(struct resman *rman, int res_id)
|
nuclear@11
|
183 {
|
nuclear@11
|
184 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
|
nuclear@11
|
185 return rman->res[res_id]->name;
|
nuclear@11
|
186 }
|
nuclear@11
|
187 return 0;
|
nuclear@11
|
188 }
|
nuclear@5
|
189
|
nuclear@5
|
190 void resman_set_res_data(struct resman *rman, int res_id, void *data)
|
nuclear@5
|
191 {
|
nuclear@5
|
192 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
|
nuclear@11
|
193 rman->res[res_id]->data = data;
|
nuclear@5
|
194 }
|
nuclear@5
|
195 }
|
nuclear@5
|
196
|
nuclear@5
|
197 void *resman_get_res_data(struct resman *rman, int res_id)
|
nuclear@5
|
198 {
|
nuclear@5
|
199 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
|
nuclear@11
|
200 return rman->res[res_id]->data;
|
nuclear@5
|
201 }
|
nuclear@5
|
202 return 0;
|
nuclear@5
|
203 }
|
nuclear@5
|
204
|
nuclear@11
|
205 int resman_get_res_result(struct resman *rman, int res_id)
|
nuclear@10
|
206 {
|
nuclear@10
|
207 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
|
nuclear@11
|
208 return rman->res[res_id]->result;
|
nuclear@10
|
209 }
|
nuclear@10
|
210 return -1;
|
nuclear@10
|
211 }
|
nuclear@10
|
212
|
nuclear@21
|
213 int resman_get_res_load_count(struct resman *rman, int res_id)
|
nuclear@21
|
214 {
|
nuclear@21
|
215 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
|
nuclear@21
|
216 return rman->res[res_id]->num_loads;
|
nuclear@21
|
217 }
|
nuclear@21
|
218 return -1;
|
nuclear@21
|
219 }
|
nuclear@21
|
220
|
nuclear@5
|
221 static int find_resource(struct resman *rman, const char *fname)
|
nuclear@5
|
222 {
|
nuclear@5
|
223 int i, sz = dynarr_size(rman->res);
|
nuclear@5
|
224
|
nuclear@5
|
225 for(i=0; i<sz; i++) {
|
nuclear@11
|
226 if(strcmp(rman->res[i]->name, fname) == 0) {
|
nuclear@5
|
227 return i;
|
nuclear@5
|
228 }
|
nuclear@5
|
229 }
|
nuclear@5
|
230 return -1;
|
nuclear@5
|
231 }
|
nuclear@5
|
232
|
nuclear@5
|
233 static int add_resource(struct resman *rman, const char *fname, void *data)
|
nuclear@5
|
234 {
|
nuclear@13
|
235 int i, idx = -1, size = dynarr_size(rman->res);
|
nuclear@11
|
236 struct resource *res;
|
nuclear@11
|
237 struct resource **tmparr;
|
nuclear@5
|
238
|
nuclear@13
|
239 /* allocate a new resource */
|
nuclear@11
|
240 if(!(res = malloc(sizeof *res))) {
|
nuclear@5
|
241 return -1;
|
nuclear@5
|
242 }
|
nuclear@13
|
243 memset(res, 0, sizeof *res);
|
nuclear@13
|
244
|
nuclear@11
|
245 res->name = strdup(fname);
|
nuclear@11
|
246 assert(res->name);
|
nuclear@11
|
247 res->data = data;
|
nuclear@21
|
248 pthread_mutex_init(&res->lock, 0);
|
nuclear@13
|
249
|
nuclear@13
|
250 /* check to see if there's an emtpy (previously erased) slot */
|
nuclear@13
|
251 for(i=0; i<size; i++) {
|
nuclear@13
|
252 if(!rman->res[i]) {
|
nuclear@13
|
253 idx = i;
|
nuclear@13
|
254 break;
|
nuclear@13
|
255 }
|
nuclear@11
|
256 }
|
nuclear@13
|
257
|
nuclear@13
|
258 if(idx == -1) {
|
nuclear@13
|
259 /* free slot not found, append a new one */
|
nuclear@13
|
260 idx = size;
|
nuclear@13
|
261
|
nuclear@13
|
262 if(!(tmparr = dynarr_push(rman->res, &res))) {
|
nuclear@13
|
263 free(res);
|
nuclear@13
|
264 return -1;
|
nuclear@13
|
265 }
|
nuclear@13
|
266 rman->res = tmparr;
|
nuclear@13
|
267 } else {
|
nuclear@13
|
268 /* free slot found, just use it */
|
nuclear@13
|
269 res = rman->res[idx];
|
nuclear@13
|
270 }
|
nuclear@13
|
271
|
nuclear@13
|
272 res->id = idx; /* set the resource id */
|
nuclear@5
|
273
|
nuclear@10
|
274 /* start a loading job ... */
|
nuclear@11
|
275 tpool_add_work(rman->tpool, rman->res[idx]);
|
nuclear@5
|
276 return idx;
|
nuclear@5
|
277 }
|
nuclear@10
|
278
|
nuclear@13
|
279 /* remove a resource and leave the pointer null to reuse the slot */
|
nuclear@13
|
280 static void remove_resource(struct resman *rman, int idx)
|
nuclear@13
|
281 {
|
nuclear@22
|
282 resman_stop_watch(rman, rman->res[idx]);
|
nuclear@21
|
283
|
nuclear@13
|
284 if(rman->destroy_func) {
|
nuclear@13
|
285 rman->destroy_func(idx, rman->destroy_func_cls);
|
nuclear@13
|
286 }
|
nuclear@13
|
287
|
nuclear@21
|
288 pthread_mutex_destroy(&rman->res[idx]->lock);
|
nuclear@13
|
289
|
nuclear@13
|
290 free(rman->res[idx]);
|
nuclear@13
|
291 rman->res[idx] = 0;
|
nuclear@13
|
292 }
|
nuclear@13
|
293
|
nuclear@10
|
294 /* this is the background work function which handles all the
|
nuclear@10
|
295 * first-stage resource loading...
|
nuclear@10
|
296 */
|
nuclear@10
|
297 static void work_func(void *data, void *cls)
|
nuclear@10
|
298 {
|
nuclear@10
|
299 struct resource *res = data;
|
nuclear@10
|
300 struct resman *rman = cls;
|
nuclear@10
|
301
|
nuclear@21
|
302 pthread_mutex_lock(&res->lock);
|
nuclear@21
|
303
|
nuclear@11
|
304 res->result = rman->load_func(res->name, res->id, rman->load_func_cls);
|
nuclear@21
|
305 if(!rman->done_func) {
|
nuclear@21
|
306 if(res->result == -1) {
|
nuclear@21
|
307 /* if there's no done function and we got an error, mark this
|
nuclear@21
|
308 * resource for deletion in the caller context. But only if this
|
nuclear@21
|
309 * is the first load of this resource.
|
nuclear@21
|
310 */
|
nuclear@21
|
311 if(res->num_loads == 0) {
|
nuclear@21
|
312 res->delete_pending = 1;
|
nuclear@21
|
313 }
|
nuclear@21
|
314 } else {
|
nuclear@21
|
315 /* succeded, start a watch */
|
nuclear@21
|
316 if(res->nfd <= 0) {
|
nuclear@22
|
317 resman_start_watch(rman, res);
|
nuclear@21
|
318 }
|
nuclear@21
|
319 }
|
nuclear@21
|
320 } else {
|
nuclear@21
|
321 /* if we have a done_func, mark this resource as done */
|
nuclear@21
|
322 res->done_pending = 1;
|
nuclear@21
|
323 }
|
nuclear@21
|
324 pthread_mutex_unlock(&res->lock);
|
nuclear@21
|
325 }
|