libresman

view src/resman.c @ 26:6b9974a8bdae

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