libresman

annotate src/resman.c @ 22:174ddb6bf92a

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