libresman

view src/resman.c @ 13:a42888d26839

bit more progress...
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 08 Feb 2014 07:41:39 +0200
parents 84f55eab27cb
children 2fcd1fbb0d18
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 pthread_mutex_t done_lock;
18 };
20 struct resman {
21 struct resource **res;
22 struct thread_pool *tpool;
24 pthread_mutex_t lock; /* global resman lock (for res array changes) */
26 resman_load_func load_func;
27 resman_done_func done_func;
28 resman_destroy_func destroy_func;
30 void *load_func_cls;
31 void *done_func_cls;
32 void *destroy_func_cls;
33 };
36 static int find_resource(struct resman *rman, const char *fname);
37 static int add_resource(struct resman *rman, const char *fname, void *data);
38 static void remove_resource(struct resman *rman, int idx);
39 static void work_func(void *data, void *cls);
41 struct resman *resman_create(void)
42 {
43 struct resman *rman = malloc(sizeof *rman);
44 if(resman_init(rman) == -1) {
45 free(rman);
46 return 0;
47 }
48 return rman;
49 }
51 void resman_free(struct resman *rman)
52 {
53 resman_destroy(rman);
54 free(rman);
55 }
57 int resman_init(struct resman *rman)
58 {
59 memset(rman, 0, sizeof *rman);
61 if(!(rman->tpool = tpool_create(TPOOL_AUTO))) {
62 return -1;
63 }
64 tpool_set_work_func(rman->tpool, work_func, rman);
66 if(!(rman->res = dynarr_alloc(0, sizeof *rman->res))) {
67 tpool_free(rman->tpool);
68 return -1;
69 }
71 pthread_mutex_init(&rman->lock, 0);
73 return 0;
74 }
76 void resman_destroy(struct resman *rman)
77 {
78 int i;
79 if(!rman) return;
81 for(i=0; i<dynarr_size(rman->res); i++) {
82 if(rman->destroy_func) {
83 rman->destroy_func(i, rman->destroy_func_cls);
84 }
85 free(rman->res[i]);
86 }
87 dynarr_free(rman->res);
89 tpool_free(rman->tpool);
91 pthread_mutex_destroy(&rman->lock);
92 }
95 void resman_set_load_func(struct resman *rman, resman_load_func func, void *cls)
96 {
97 rman->load_func = func;
98 rman->load_func_cls = cls;
99 }
101 void resman_set_done_func(struct resman *rman, resman_done_func func, void *cls)
102 {
103 rman->done_func = func;
104 rman->done_func_cls = cls;
105 }
107 void resman_set_destroy_func(struct resman *rman, resman_destroy_func func, void *cls)
108 {
109 rman->destroy_func = func;
110 rman->destroy_func_cls = cls;
111 }
113 int resman_lookup(struct resman *rman, const char *fname, void *data)
114 {
115 int ridx;
117 if((ridx = find_resource(rman, fname)) != -1) {
118 return ridx;
119 }
121 /* resource not found, create a new one and start a loading job */
122 return add_resource(rman, fname, data);
123 }
125 void resman_wait(struct resman *rman, int id)
126 {
127 /* TODO */
128 }
130 int resman_poll(struct resman *rman)
131 {
132 int i, num_res;
134 if(!rman->done_func) {
135 return 0; /* no done callback; there's no point in checking anything */
136 }
138 num_res = dynarr_size(rman->res);
139 for(i=0; i<num_res; i++) {
140 struct resource *res = rman->res[i];
141 if(!res) {
142 continue;
143 }
145 printf("locking mutex %d\n", res->id);
146 pthread_mutex_lock(&res->done_lock);
147 if(!res->done_pending) {
148 printf(" unlocking mutex %d\n", res->id);
149 pthread_mutex_unlock(&res->done_lock);
150 continue;
151 }
153 /* so a done callback *is* pending... */
154 res->done_pending = 0;
155 if(rman->done_func(i, rman->done_func_cls) == -1) {
156 /* done-func returned -1, so let's remove the resource */
157 printf(" unlocking mutex %d\n", res->id);
158 pthread_mutex_unlock(&res->done_lock);
159 remove_resource(rman, i);
160 continue;
161 }
162 printf(" unlocking mutex %d\n", res->id);
163 pthread_mutex_unlock(&res->done_lock);
164 }
165 return 0;
166 }
168 const char *resman_get_res_name(struct resman *rman, int res_id)
169 {
170 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
171 return rman->res[res_id]->name;
172 }
173 return 0;
174 }
176 void resman_set_res_data(struct resman *rman, int res_id, void *data)
177 {
178 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
179 rman->res[res_id]->data = data;
180 }
181 }
183 void *resman_get_res_data(struct resman *rman, int res_id)
184 {
185 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
186 return rman->res[res_id]->data;
187 }
188 return 0;
189 }
191 int resman_get_res_result(struct resman *rman, int res_id)
192 {
193 if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
194 return rman->res[res_id]->result;
195 }
196 return -1;
197 }
199 static int find_resource(struct resman *rman, const char *fname)
200 {
201 int i, sz = dynarr_size(rman->res);
203 for(i=0; i<sz; i++) {
204 if(strcmp(rman->res[i]->name, fname) == 0) {
205 return i;
206 }
207 }
208 return -1;
209 }
211 static int add_resource(struct resman *rman, const char *fname, void *data)
212 {
213 int i, idx = -1, size = dynarr_size(rman->res);
214 struct resource *res;
215 struct resource **tmparr;
217 /* allocate a new resource */
218 if(!(res = malloc(sizeof *res))) {
219 return -1;
220 }
221 memset(res, 0, sizeof *res);
223 res->name = strdup(fname);
224 assert(res->name);
225 res->data = data;
226 pthread_mutex_init(&res->done_lock, 0);
229 /* check to see if there's an emtpy (previously erased) slot */
230 for(i=0; i<size; i++) {
231 if(!rman->res[i]) {
232 idx = i;
233 break;
234 }
235 }
237 if(idx == -1) {
238 /* free slot not found, append a new one */
239 idx = size;
241 if(!(tmparr = dynarr_push(rman->res, &res))) {
242 free(res);
243 return -1;
244 }
245 rman->res = tmparr;
246 } else {
247 /* free slot found, just use it */
248 res = rman->res[idx];
249 }
251 res->id = idx; /* set the resource id */
253 /* start a loading job ... */
254 tpool_add_work(rman->tpool, rman->res[idx]);
255 return idx;
256 }
258 /* remove a resource and leave the pointer null to reuse the slot */
259 static void remove_resource(struct resman *rman, int idx)
260 {
261 if(rman->destroy_func) {
262 rman->destroy_func(idx, rman->destroy_func_cls);
263 }
265 pthread_mutex_destroy(&rman->res[idx]->done_lock);
267 free(rman->res[idx]);
268 rman->res[idx] = 0;
269 }
271 /* this is the background work function which handles all the
272 * first-stage resource loading...
273 */
274 static void work_func(void *data, void *cls)
275 {
276 struct resource *res = data;
277 struct resman *rman = cls;
279 res->result = rman->load_func(res->name, res->id, rman->load_func_cls);
280 if(res->result == -1 && !rman->done_func) {
281 /* if there's no done function and we got an error, remove the resource now */
282 remove_resource(rman, res->id);
283 return;
284 }
286 printf("locking mutex %d\n", res->id);
287 pthread_mutex_lock(&res->done_lock);
288 res->done_pending = 1;
289 printf(" unlocking mutex %d\n", res->id);
290 pthread_mutex_unlock(&res->done_lock);
291 }