libresman

annotate 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
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 }