libresman

view src/resman.c @ 20:c6073bf9fd38

finally! async loading works. Now on to inotify...
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 12 Feb 2014 16:02:12 +0200
parents 2fcd1fbb0d18
children fe0dbdfbe403
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 "dynarr.h"
8 #include "threadpool.h"
10 struct resource {
11 int id;
12 char *name;
13 void *data;
14 int result; /* last callback-reported success/fail code */
16 int done_pending;
17 int delete_pending;
18 pthread_mutex_t done_lock;
19 };
21 struct resman {
22 struct resource **res;
23 struct thread_pool *tpool;
25 pthread_mutex_t lock; /* global resman lock (for res array changes) */
27 resman_load_func load_func;
28 resman_done_func done_func;
29 resman_destroy_func destroy_func;
31 void *load_func_cls;
32 void *done_func_cls;
33 void *destroy_func_cls;
34 };
37 static int find_resource(struct resman *rman, const char *fname);
38 static int add_resource(struct resman *rman, const char *fname, void *data);
39 static void remove_resource(struct resman *rman, int idx);
40 static void work_func(void *data, void *cls);
42 struct resman *resman_create(void)
43 {
44 struct resman *rman = malloc(sizeof *rman);
45 if(resman_init(rman) == -1) {
46 free(rman);
47 return 0;
48 }
49 return rman;
50 }
52 void resman_free(struct resman *rman)
53 {
54 resman_destroy(rman);
55 free(rman);
56 }
58 int resman_init(struct resman *rman)
59 {
60 const char *env;
61 int num_threads = TPOOL_AUTO;
63 memset(rman, 0, sizeof *rman);
65 if((env = getenv("RESMAN_THREADS"))) {
66 num_threads = atoi(env);
67 }
69 if(!(rman->tpool = tpool_create(num_threads))) {
70 return -1;
71 }
72 tpool_set_work_func(rman->tpool, work_func, rman);
74 if(!(rman->res = dynarr_alloc(0, sizeof *rman->res))) {
75 tpool_free(rman->tpool);
76 return -1;
77 }
79 pthread_mutex_init(&rman->lock, 0);
81 return 0;
82 }
84 void resman_destroy(struct resman *rman)
85 {
86 int i;
87 if(!rman) return;
89 for(i=0; i<dynarr_size(rman->res); i++) {
90 if(rman->destroy_func) {
91 rman->destroy_func(i, rman->destroy_func_cls);
92 }
93 free(rman->res[i]);
94 }
95 dynarr_free(rman->res);
97 tpool_free(rman->tpool);
99 pthread_mutex_destroy(&rman->lock);
100 }
103 void resman_set_load_func(struct resman *rman, resman_load_func func, void *cls)
104 {
105 rman->load_func = func;
106 rman->load_func_cls = cls;
107 }
109 void resman_set_done_func(struct resman *rman, resman_done_func func, void *cls)
110 {
111 rman->done_func = func;
112 rman->done_func_cls = cls;
113 }
115 void resman_set_destroy_func(struct resman *rman, resman_destroy_func func, void *cls)
116 {
117 rman->destroy_func = func;
118 rman->destroy_func_cls = cls;
119 }
121 int resman_lookup(struct resman *rman, const char *fname, void *data)
122 {
123 int ridx;
125 if((ridx = find_resource(rman, fname)) != -1) {
126 return ridx;
127 }
129 /* resource not found, create a new one and start a loading job */
130 return add_resource(rman, fname, data);
131 }
133 void resman_wait(struct resman *rman, int id)
134 {
135 /* TODO */
136 }
138 int resman_poll(struct resman *rman)
139 {
140 int i, num_res;
142 /* first check all the resources to see if any is pending deletion */
143 num_res = dynarr_size(rman->res);
144 for(i=0; i<num_res; i++) {
145 struct resource *res = rman->res[i];
146 if(!res) {
147 continue;
148 }
150 if(res->delete_pending) {
151 if(rman->destroy_func) {
152 rman->destroy_func(i, rman->destroy_func_cls);
153 }
154 remove_resource(rman, i);
155 }
156 }
159 if(!rman->done_func) {
160 return 0; /* no done callback; there's no point in checking anything */
161 }
163 for(i=0; i<num_res; i++) {
164 struct resource *res = rman->res[i];
165 if(!res) {
166 continue;
167 }
169 pthread_mutex_lock(&res->done_lock);
170 if(!res->done_pending) {
171 pthread_mutex_unlock(&res->done_lock);
172 continue;
173 }
175 /* so a done callback *is* pending... */
176 res->done_pending = 0;
177 if(rman->done_func(i, rman->done_func_cls) == -1) {
178 /* done-func returned -1, so let's remove the resource */
179 pthread_mutex_unlock(&res->done_lock);
180 remove_resource(rman, i);
181 continue;
182 }
183 pthread_mutex_unlock(&res->done_lock);
184 }
185 return 0;
186 }
188 const char *resman_get_res_name(struct resman *rman, int res_id)
189 {
190 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
191 return rman->res[res_id]->name;
192 }
193 return 0;
194 }
196 void resman_set_res_data(struct resman *rman, int res_id, void *data)
197 {
198 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
199 rman->res[res_id]->data = data;
200 }
201 }
203 void *resman_get_res_data(struct resman *rman, int res_id)
204 {
205 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
206 return rman->res[res_id]->data;
207 }
208 return 0;
209 }
211 int resman_get_res_result(struct resman *rman, int res_id)
212 {
213 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
214 return rman->res[res_id]->result;
215 }
216 return -1;
217 }
219 static int find_resource(struct resman *rman, const char *fname)
220 {
221 int i, sz = dynarr_size(rman->res);
223 for(i=0; i<sz; i++) {
224 if(strcmp(rman->res[i]->name, fname) == 0) {
225 return i;
226 }
227 }
228 return -1;
229 }
231 static int add_resource(struct resman *rman, const char *fname, void *data)
232 {
233 int i, idx = -1, size = dynarr_size(rman->res);
234 struct resource *res;
235 struct resource **tmparr;
237 /* allocate a new resource */
238 if(!(res = malloc(sizeof *res))) {
239 return -1;
240 }
241 memset(res, 0, sizeof *res);
243 res->name = strdup(fname);
244 assert(res->name);
245 res->data = data;
246 pthread_mutex_init(&res->done_lock, 0);
249 /* check to see if there's an emtpy (previously erased) slot */
250 for(i=0; i<size; i++) {
251 if(!rman->res[i]) {
252 idx = i;
253 break;
254 }
255 }
257 if(idx == -1) {
258 /* free slot not found, append a new one */
259 idx = size;
261 if(!(tmparr = dynarr_push(rman->res, &res))) {
262 free(res);
263 return -1;
264 }
265 rman->res = tmparr;
266 } else {
267 /* free slot found, just use it */
268 res = rman->res[idx];
269 }
271 res->id = idx; /* set the resource id */
273 /* start a loading job ... */
274 tpool_add_work(rman->tpool, rman->res[idx]);
275 return idx;
276 }
278 /* remove a resource and leave the pointer null to reuse the slot */
279 static void remove_resource(struct resman *rman, int idx)
280 {
281 if(rman->destroy_func) {
282 rman->destroy_func(idx, rman->destroy_func_cls);
283 }
285 pthread_mutex_destroy(&rman->res[idx]->done_lock);
287 free(rman->res[idx]);
288 rman->res[idx] = 0;
289 }
291 /* this is the background work function which handles all the
292 * first-stage resource loading...
293 */
294 static void work_func(void *data, void *cls)
295 {
296 struct resource *res = data;
297 struct resman *rman = cls;
299 res->result = rman->load_func(res->name, res->id, rman->load_func_cls);
300 if(res->result == -1 && !rman->done_func) {
301 /* if there's no done function and we got an error, mark this
302 * resource for deletion in the caller context
303 */
304 res->delete_pending = 1;
305 return;
306 }
308 pthread_mutex_lock(&res->done_lock);
309 res->done_pending = 1;
310 pthread_mutex_unlock(&res->done_lock);
311 }