nuclear@5: #include nuclear@5: #include nuclear@5: #include nuclear@5: #include nuclear@10: #include nuclear@5: #include "resman.h" nuclear@5: #include "dynarr.h" nuclear@5: #include "threadpool.h" nuclear@5: nuclear@5: struct resource { nuclear@11: int id; nuclear@5: char *name; nuclear@5: void *data; nuclear@10: int result; /* last callback-reported success/fail code */ nuclear@10: nuclear@10: int done_pending; nuclear@14: int delete_pending; nuclear@10: pthread_mutex_t done_lock; nuclear@5: }; nuclear@5: nuclear@5: struct resman { nuclear@11: struct resource **res; nuclear@10: struct thread_pool *tpool; nuclear@5: nuclear@13: pthread_mutex_t lock; /* global resman lock (for res array changes) */ nuclear@13: nuclear@5: resman_load_func load_func; nuclear@10: resman_done_func done_func; nuclear@5: resman_destroy_func destroy_func; nuclear@5: nuclear@5: void *load_func_cls; nuclear@10: void *done_func_cls; nuclear@5: void *destroy_func_cls; nuclear@5: }; nuclear@5: nuclear@5: nuclear@5: static int find_resource(struct resman *rman, const char *fname); nuclear@5: static int add_resource(struct resman *rman, const char *fname, void *data); nuclear@13: static void remove_resource(struct resman *rman, int idx); nuclear@10: static void work_func(void *data, void *cls); nuclear@5: nuclear@5: struct resman *resman_create(void) nuclear@5: { nuclear@5: struct resman *rman = malloc(sizeof *rman); nuclear@5: if(resman_init(rman) == -1) { nuclear@5: free(rman); nuclear@5: return 0; nuclear@5: } nuclear@5: return rman; nuclear@5: } nuclear@5: nuclear@5: void resman_free(struct resman *rman) nuclear@5: { nuclear@5: resman_destroy(rman); nuclear@5: free(rman); nuclear@5: } nuclear@5: nuclear@5: int resman_init(struct resman *rman) nuclear@5: { nuclear@14: const char *env; nuclear@14: int num_threads = TPOOL_AUTO; nuclear@14: nuclear@5: memset(rman, 0, sizeof *rman); nuclear@5: nuclear@14: if((env = getenv("RESMAN_THREADS"))) { nuclear@14: num_threads = atoi(env); nuclear@14: } nuclear@14: nuclear@14: if(!(rman->tpool = tpool_create(num_threads))) { nuclear@10: return -1; nuclear@10: } nuclear@10: tpool_set_work_func(rman->tpool, work_func, rman); nuclear@10: nuclear@5: if(!(rman->res = dynarr_alloc(0, sizeof *rman->res))) { nuclear@10: tpool_free(rman->tpool); nuclear@5: return -1; nuclear@5: } nuclear@5: nuclear@13: pthread_mutex_init(&rman->lock, 0); nuclear@13: nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@5: void resman_destroy(struct resman *rman) nuclear@5: { nuclear@5: int i; nuclear@5: if(!rman) return; nuclear@5: nuclear@5: for(i=0; ires); i++) { nuclear@5: if(rman->destroy_func) { nuclear@11: rman->destroy_func(i, rman->destroy_func_cls); nuclear@5: } nuclear@11: free(rman->res[i]); nuclear@5: } nuclear@5: dynarr_free(rman->res); nuclear@10: nuclear@10: tpool_free(rman->tpool); nuclear@13: nuclear@13: pthread_mutex_destroy(&rman->lock); nuclear@5: } nuclear@5: nuclear@5: nuclear@5: void resman_set_load_func(struct resman *rman, resman_load_func func, void *cls) nuclear@5: { nuclear@5: rman->load_func = func; nuclear@5: rman->load_func_cls = cls; nuclear@5: } nuclear@5: nuclear@10: void resman_set_done_func(struct resman *rman, resman_done_func func, void *cls) nuclear@5: { nuclear@10: rman->done_func = func; nuclear@10: rman->done_func_cls = cls; nuclear@5: } nuclear@5: nuclear@5: void resman_set_destroy_func(struct resman *rman, resman_destroy_func func, void *cls) nuclear@5: { nuclear@5: rman->destroy_func = func; nuclear@5: rman->destroy_func_cls = cls; nuclear@5: } nuclear@5: nuclear@5: int resman_lookup(struct resman *rman, const char *fname, void *data) nuclear@5: { nuclear@5: int ridx; nuclear@5: nuclear@5: if((ridx = find_resource(rman, fname)) != -1) { nuclear@5: return ridx; nuclear@5: } nuclear@5: nuclear@5: /* resource not found, create a new one and start a loading job */ nuclear@5: return add_resource(rman, fname, data); nuclear@5: } nuclear@5: nuclear@5: void resman_wait(struct resman *rman, int id) nuclear@5: { nuclear@5: /* TODO */ nuclear@5: } nuclear@5: nuclear@5: int resman_poll(struct resman *rman) nuclear@5: { nuclear@10: int i, num_res; nuclear@10: nuclear@14: /* first check all the resources to see if any is pending deletion */ nuclear@14: num_res = dynarr_size(rman->res); nuclear@14: for(i=0; ires[i]; nuclear@14: if(!res) { nuclear@14: continue; nuclear@14: } nuclear@14: nuclear@14: if(res->delete_pending) { nuclear@14: if(rman->destroy_func) { nuclear@14: rman->destroy_func(i, rman->destroy_func_cls); nuclear@14: } nuclear@14: remove_resource(rman, i); nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: nuclear@10: if(!rman->done_func) { nuclear@10: return 0; /* no done callback; there's no point in checking anything */ nuclear@10: } nuclear@10: nuclear@10: for(i=0; ires[i]; nuclear@13: if(!res) { nuclear@13: continue; nuclear@13: } nuclear@10: nuclear@10: pthread_mutex_lock(&res->done_lock); nuclear@10: if(!res->done_pending) { nuclear@10: pthread_mutex_unlock(&res->done_lock); nuclear@10: continue; nuclear@10: } nuclear@10: nuclear@10: /* so a done callback *is* pending... */ nuclear@10: res->done_pending = 0; nuclear@13: if(rman->done_func(i, rman->done_func_cls) == -1) { nuclear@13: /* done-func returned -1, so let's remove the resource */ nuclear@13: pthread_mutex_unlock(&res->done_lock); nuclear@13: remove_resource(rman, i); nuclear@13: continue; nuclear@13: } nuclear@10: pthread_mutex_unlock(&res->done_lock); nuclear@10: } nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@11: const char *resman_get_res_name(struct resman *rman, int res_id) nuclear@11: { nuclear@11: if(res_id >= 0 && res_id < dynarr_size(rman->res)) { nuclear@11: return rman->res[res_id]->name; nuclear@11: } nuclear@11: return 0; nuclear@11: } nuclear@5: nuclear@5: void resman_set_res_data(struct resman *rman, int res_id, void *data) nuclear@5: { nuclear@5: if(res_id >= 0 && res_id < dynarr_size(rman->res)) { nuclear@11: rman->res[res_id]->data = data; nuclear@5: } nuclear@5: } nuclear@5: nuclear@5: void *resman_get_res_data(struct resman *rman, int res_id) nuclear@5: { nuclear@5: if(res_id >= 0 && res_id < dynarr_size(rman->res)) { nuclear@11: return rman->res[res_id]->data; nuclear@5: } nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@11: int resman_get_res_result(struct resman *rman, int res_id) nuclear@10: { nuclear@10: if(res_id >= 0 && res_id < dynarr_size(rman->res)) { nuclear@11: return rman->res[res_id]->result; nuclear@10: } nuclear@10: return -1; nuclear@10: } nuclear@10: nuclear@5: static int find_resource(struct resman *rman, const char *fname) nuclear@5: { nuclear@5: int i, sz = dynarr_size(rman->res); nuclear@5: nuclear@5: for(i=0; ires[i]->name, fname) == 0) { nuclear@5: return i; nuclear@5: } nuclear@5: } nuclear@5: return -1; nuclear@5: } nuclear@5: nuclear@5: static int add_resource(struct resman *rman, const char *fname, void *data) nuclear@5: { nuclear@13: int i, idx = -1, size = dynarr_size(rman->res); nuclear@11: struct resource *res; nuclear@11: struct resource **tmparr; nuclear@5: nuclear@13: /* allocate a new resource */ nuclear@11: if(!(res = malloc(sizeof *res))) { nuclear@5: return -1; nuclear@5: } nuclear@13: memset(res, 0, sizeof *res); nuclear@13: nuclear@11: res->name = strdup(fname); nuclear@11: assert(res->name); nuclear@11: res->data = data; nuclear@12: pthread_mutex_init(&res->done_lock, 0); nuclear@12: nuclear@13: nuclear@13: /* check to see if there's an emtpy (previously erased) slot */ nuclear@13: for(i=0; ires[i]) { nuclear@13: idx = i; nuclear@13: break; nuclear@13: } nuclear@11: } nuclear@13: nuclear@13: if(idx == -1) { nuclear@13: /* free slot not found, append a new one */ nuclear@13: idx = size; nuclear@13: nuclear@13: if(!(tmparr = dynarr_push(rman->res, &res))) { nuclear@13: free(res); nuclear@13: return -1; nuclear@13: } nuclear@13: rman->res = tmparr; nuclear@13: } else { nuclear@13: /* free slot found, just use it */ nuclear@13: res = rman->res[idx]; nuclear@13: } nuclear@13: nuclear@13: res->id = idx; /* set the resource id */ nuclear@5: nuclear@10: /* start a loading job ... */ nuclear@11: tpool_add_work(rman->tpool, rman->res[idx]); nuclear@5: return idx; nuclear@5: } nuclear@10: nuclear@13: /* remove a resource and leave the pointer null to reuse the slot */ nuclear@13: static void remove_resource(struct resman *rman, int idx) nuclear@13: { nuclear@13: if(rman->destroy_func) { nuclear@13: rman->destroy_func(idx, rman->destroy_func_cls); nuclear@13: } nuclear@13: nuclear@13: pthread_mutex_destroy(&rman->res[idx]->done_lock); nuclear@13: nuclear@13: free(rman->res[idx]); nuclear@13: rman->res[idx] = 0; nuclear@13: } nuclear@13: nuclear@10: /* this is the background work function which handles all the nuclear@10: * first-stage resource loading... nuclear@10: */ nuclear@10: static void work_func(void *data, void *cls) nuclear@10: { nuclear@10: struct resource *res = data; nuclear@10: struct resman *rman = cls; nuclear@10: nuclear@11: res->result = rman->load_func(res->name, res->id, rman->load_func_cls); nuclear@13: if(res->result == -1 && !rman->done_func) { nuclear@14: /* if there's no done function and we got an error, mark this nuclear@14: * resource for deletion in the caller context nuclear@14: */ nuclear@14: res->delete_pending = 1; nuclear@13: return; nuclear@13: } nuclear@12: nuclear@10: pthread_mutex_lock(&res->done_lock); nuclear@10: res->done_pending = 1; nuclear@10: pthread_mutex_unlock(&res->done_lock); nuclear@10: }