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