libresman

changeset 21:fe0dbdfbe403

file modification monitoring and reload done on linux
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 12 Feb 2014 22:05:28 +0200
parents c6073bf9fd38
children 174ddb6bf92a
files examples/imgthumbs/src/thumbs.c examples/imgthumbs/src/thumbs.h resman.def src/rbtree.c src/rbtree.h src/resman.c src/resman.h src/threadpool.c
diffstat 8 files changed, 784 insertions(+), 33 deletions(-) [+]
line diff
     1.1 --- a/examples/imgthumbs/src/thumbs.c	Wed Feb 12 16:02:12 2014 +0200
     1.2 +++ b/examples/imgthumbs/src/thumbs.c	Wed Feb 12 22:05:28 2014 +0200
     1.3 @@ -22,6 +22,11 @@
     1.4  	struct dirent *dent;
     1.5  	/* allocate dummy head node */
     1.6  	struct thumbnail *list = calloc(1, sizeof *list);
     1.7 +	char *env;
     1.8 +
     1.9 +	if((env = getenv("RESMAN_LOAD_ASYNC"))) {
    1.10 +		dbg_load_async = atoi(env);
    1.11 +	}
    1.12  
    1.13  	if(!texman) {
    1.14  		texman = resman_create();
    1.15 @@ -85,6 +90,7 @@
    1.16  			list->next = node;
    1.17  		}
    1.18  		node->list = list;
    1.19 +		node->load_count = 0;
    1.20  	}
    1.21  	closedir(dir);
    1.22  
    1.23 @@ -243,11 +249,13 @@
    1.24  			thumb->img->width, thumb->img->height, 0, img_glfmt(thumb->img),
    1.25  			img_gltype(thumb->img), thumb->img->pixels);
    1.26  
    1.27 -	/* and add it to the list of thumbnails */
    1.28 -	thumb->prev = thumb->list;
    1.29 -	thumb->next = thumb->list->next;
    1.30 -	if(thumb->list->next) thumb->list->next->prev = thumb;
    1.31 -	thumb->list->next = thumb;
    1.32 +	/* and add it to the list of thumbnails (if it's the first loading) */
    1.33 +	if(resman_get_res_load_count(texman, id) == 0) {
    1.34 +		thumb->prev = thumb->list;
    1.35 +		thumb->next = thumb->list->next;
    1.36 +		if(thumb->list->next) thumb->list->next->prev = thumb;
    1.37 +		thumb->list->next = thumb;
    1.38 +	}
    1.39  	return 0;
    1.40  }
    1.41  
     2.1 --- a/examples/imgthumbs/src/thumbs.h	Wed Feb 12 16:02:12 2014 +0200
     2.2 +++ b/examples/imgthumbs/src/thumbs.h	Wed Feb 12 22:05:28 2014 +0200
     2.3 @@ -15,6 +15,8 @@
     2.4  
     2.5  	struct thumbnail *next, *prev;
     2.6  	struct thumbnail *list;	/* pointer to the list this thumbnail belongs to */
     2.7 +
     2.8 +	int load_count;
     2.9  };
    2.10  
    2.11  struct thumbnail *create_thumbs(const char *dirpath);
     3.1 --- a/resman.def	Wed Feb 12 16:02:12 2014 +0200
     3.2 +++ b/resman.def	Wed Feb 12 22:05:28 2014 +0200
     3.3 @@ -18,4 +18,5 @@
     3.4  	resman_get_res_name
     3.5  	resman_set_res_data
     3.6  	resman_get_res_data
     3.7 -	resman_get_res_result
     3.8 \ No newline at end of file
     3.9 +	resman_get_res_result
    3.10 +	resman_get_res_load_count
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/rbtree.c	Wed Feb 12 22:05:28 2014 +0200
     4.3 @@ -0,0 +1,494 @@
     4.4 +#include <stdio.h>
     4.5 +#include <stdlib.h>
     4.6 +#include <stdint.h>
     4.7 +#include <string.h>
     4.8 +#include "rbtree.h"
     4.9 +
    4.10 +#define INT2PTR(x)	((void*)(intptr_t)(x))
    4.11 +#define PTR2INT(x)	((int)(intptr_t)(x))
    4.12 +
    4.13 +struct rbtree {
    4.14 +	struct rbnode *root;
    4.15 +
    4.16 +	rb_alloc_func_t alloc;
    4.17 +	rb_free_func_t free;
    4.18 +
    4.19 +	rb_cmp_func_t cmp;
    4.20 +	rb_del_func_t del;
    4.21 +	void *del_cls;
    4.22 +
    4.23 +	struct rbnode *rstack, *iter;
    4.24 +};
    4.25 +
    4.26 +static int cmpaddr(const void *ap, const void *bp);
    4.27 +static int cmpint(const void *ap, const void *bp);
    4.28 +
    4.29 +static int count_nodes(struct rbnode *node);
    4.30 +static void del_tree(struct rbnode *node, void (*delfunc)(struct rbnode*, void*), void *cls);
    4.31 +static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data);
    4.32 +static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key);
    4.33 +/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key);*/
    4.34 +static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls);
    4.35 +
    4.36 +struct rbtree *rb_create(rb_cmp_func_t cmp_func)
    4.37 +{
    4.38 +	struct rbtree *rb;
    4.39 +
    4.40 +	if(!(rb = malloc(sizeof *rb))) {
    4.41 +		return 0;
    4.42 +	}
    4.43 +	if(rb_init(rb, cmp_func) == -1) {
    4.44 +		free(rb);
    4.45 +		return 0;
    4.46 +	}
    4.47 +	return rb;
    4.48 +}
    4.49 +
    4.50 +void rb_free(struct rbtree *rb)
    4.51 +{
    4.52 +	rb_destroy(rb);
    4.53 +	free(rb);
    4.54 +}
    4.55 +
    4.56 +
    4.57 +int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func)
    4.58 +{
    4.59 +	memset(rb, 0, sizeof *rb);
    4.60 +
    4.61 +	if(cmp_func == RB_KEY_INT) {
    4.62 +		rb->cmp = cmpint;
    4.63 +	} else if(cmp_func == RB_KEY_STRING) {
    4.64 +		rb->cmp = (rb_cmp_func_t)strcmp;
    4.65 +	} else {
    4.66 +		rb->cmp = cmpaddr;
    4.67 +	}
    4.68 +
    4.69 +	rb->alloc = malloc;
    4.70 +	rb->free = free;
    4.71 +	return 0;
    4.72 +}
    4.73 +
    4.74 +void rb_destroy(struct rbtree *rb)
    4.75 +{
    4.76 +	del_tree(rb->root, rb->del, rb->del_cls);
    4.77 +}
    4.78 +
    4.79 +void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free)
    4.80 +{
    4.81 +	rb->alloc = alloc;
    4.82 +	rb->free = free;
    4.83 +}
    4.84 +
    4.85 +
    4.86 +void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func)
    4.87 +{
    4.88 +	rb->cmp = func;
    4.89 +}
    4.90 +
    4.91 +void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls)
    4.92 +{
    4.93 +	rb->del = func;
    4.94 +	rb->del_cls = cls;
    4.95 +}
    4.96 +
    4.97 +
    4.98 +void rb_clear(struct rbtree *rb)
    4.99 +{
   4.100 +	del_tree(rb->root, rb->del, rb->del_cls);
   4.101 +	rb->root = 0;
   4.102 +}
   4.103 +
   4.104 +int rb_copy(struct rbtree *dest, struct rbtree *src)
   4.105 +{
   4.106 +	struct rbnode *node;
   4.107 +
   4.108 +	rb_clear(dest);
   4.109 +	rb_begin(src);
   4.110 +	while((node = rb_next(src))) {
   4.111 +		if(rb_insert(dest, node->key, node->data) == -1) {
   4.112 +			return -1;
   4.113 +		}
   4.114 +	}
   4.115 +	return 0;
   4.116 +}
   4.117 +
   4.118 +int rb_size(struct rbtree *rb)
   4.119 +{
   4.120 +	return count_nodes(rb->root);
   4.121 +}
   4.122 +
   4.123 +int rb_insert(struct rbtree *rb, void *key, void *data)
   4.124 +{
   4.125 +	rb->root = insert(rb, rb->root, key, data);
   4.126 +	rb->root->red = 0;
   4.127 +	return 0;
   4.128 +}
   4.129 +
   4.130 +int rb_inserti(struct rbtree *rb, int key, void *data)
   4.131 +{
   4.132 +	rb->root = insert(rb, rb->root, INT2PTR(key), data);
   4.133 +	rb->root->red = 0;
   4.134 +	return 0;
   4.135 +}
   4.136 +
   4.137 +
   4.138 +int rb_delete(struct rbtree *rb, void *key)
   4.139 +{
   4.140 +	rb->root = delete(rb, rb->root, key);
   4.141 +	rb->root->red = 0;
   4.142 +	return 0;
   4.143 +}
   4.144 +
   4.145 +int rb_deletei(struct rbtree *rb, int key)
   4.146 +{
   4.147 +	rb->root = delete(rb, rb->root, INT2PTR(key));
   4.148 +	rb->root->red = 0;
   4.149 +	return 0;
   4.150 +}
   4.151 +
   4.152 +
   4.153 +void *rb_find(struct rbtree *rb, void *key)
   4.154 +{
   4.155 +	struct rbnode *node = rb->root;
   4.156 +
   4.157 +	while(node) {
   4.158 +		int cmp = rb->cmp(key, node->key);
   4.159 +		if(cmp == 0) {
   4.160 +			return node->data;
   4.161 +		}
   4.162 +		node = cmp < 0 ? node->left : node->right;
   4.163 +	}
   4.164 +	return 0;
   4.165 +}
   4.166 +
   4.167 +void *rb_findi(struct rbtree *rb, int key)
   4.168 +{
   4.169 +	return rb_find(rb, INT2PTR(key));
   4.170 +}
   4.171 +
   4.172 +
   4.173 +void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls)
   4.174 +{
   4.175 +	traverse(rb->root, func, cls);
   4.176 +}
   4.177 +
   4.178 +
   4.179 +struct rbnode *rb_root(struct rbtree *rb)
   4.180 +{
   4.181 +	return rb->root;
   4.182 +}
   4.183 +
   4.184 +void rb_begin(struct rbtree *rb)
   4.185 +{
   4.186 +	rb->rstack = 0;
   4.187 +	rb->iter = rb->root;
   4.188 +}
   4.189 +
   4.190 +#define push(sp, x)		((x)->next = (sp), (sp) = (x))
   4.191 +#define pop(sp)			((sp) = (sp)->next)
   4.192 +#define top(sp)			(sp)
   4.193 +
   4.194 +struct rbnode *rb_next(struct rbtree *rb)
   4.195 +{
   4.196 +	struct rbnode *res = 0;
   4.197 +
   4.198 +	while(rb->rstack || rb->iter) {
   4.199 +		if(rb->iter) {
   4.200 +			push(rb->rstack, rb->iter);
   4.201 +			rb->iter = rb->iter->left;
   4.202 +		} else {
   4.203 +			rb->iter = top(rb->rstack);
   4.204 +			pop(rb->rstack);
   4.205 +			res = rb->iter;
   4.206 +			rb->iter = rb->iter->right;
   4.207 +			break;
   4.208 +		}
   4.209 +	}
   4.210 +	return res;
   4.211 +}
   4.212 +
   4.213 +void *rb_node_key(struct rbnode *node)
   4.214 +{
   4.215 +	return node ? node->key : 0;
   4.216 +}
   4.217 +
   4.218 +int rb_node_keyi(struct rbnode *node)
   4.219 +{
   4.220 +	return node ? PTR2INT(node->key) : 0;
   4.221 +}
   4.222 +
   4.223 +void *rb_node_data(struct rbnode *node)
   4.224 +{
   4.225 +	return node ? node->data : 0;
   4.226 +}
   4.227 +
   4.228 +static int cmpaddr(const void *ap, const void *bp)
   4.229 +{
   4.230 +	return ap < bp ? -1 : (ap > bp ? 1 : 0);
   4.231 +}
   4.232 +
   4.233 +static int cmpint(const void *ap, const void *bp)
   4.234 +{
   4.235 +	return PTR2INT(ap) - PTR2INT(bp);
   4.236 +}
   4.237 +
   4.238 +
   4.239 +/* ---- left-leaning 2-3 red-black implementation ---- */
   4.240 +
   4.241 +/* helper prototypes */
   4.242 +static int is_red(struct rbnode *tree);
   4.243 +static void color_flip(struct rbnode *tree);
   4.244 +static struct rbnode *rot_left(struct rbnode *a);
   4.245 +static struct rbnode *rot_right(struct rbnode *a);
   4.246 +static struct rbnode *find_min(struct rbnode *tree);
   4.247 +static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree);
   4.248 +/*static struct rbnode *move_red_right(struct rbnode *tree);*/
   4.249 +static struct rbnode *move_red_left(struct rbnode *tree);
   4.250 +static struct rbnode *fix_up(struct rbnode *tree);
   4.251 +
   4.252 +static int count_nodes(struct rbnode *node)
   4.253 +{
   4.254 +	if(!node)
   4.255 +		return 0;
   4.256 +
   4.257 +	return 1 + count_nodes(node->left) + count_nodes(node->right);
   4.258 +}
   4.259 +
   4.260 +static void del_tree(struct rbnode *node, rb_del_func_t delfunc, void *cls)
   4.261 +{
   4.262 +	if(!node)
   4.263 +		return;
   4.264 +
   4.265 +	del_tree(node->left, delfunc, cls);
   4.266 +	del_tree(node->right, delfunc, cls);
   4.267 +
   4.268 +	if(delfunc) {
   4.269 +		delfunc(node, cls);
   4.270 +	}
   4.271 +	free(node);
   4.272 +}
   4.273 +
   4.274 +static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data)
   4.275 +{
   4.276 +	int cmp;
   4.277 +
   4.278 +	if(!tree) {
   4.279 +		struct rbnode *node = rb->alloc(sizeof *node);
   4.280 +		node->red = 1;
   4.281 +		node->key = key;
   4.282 +		node->data = data;
   4.283 +		node->left = node->right = 0;
   4.284 +		return node;
   4.285 +	}
   4.286 +
   4.287 +	cmp = rb->cmp(key, tree->key);
   4.288 +
   4.289 +	if(cmp < 0) {
   4.290 +		tree->left = insert(rb, tree->left, key, data);
   4.291 +	} else if(cmp > 0) {
   4.292 +		tree->right = insert(rb, tree->right, key, data);
   4.293 +	} else {
   4.294 +		tree->data = data;
   4.295 +	}
   4.296 +
   4.297 +	/* fix right-leaning reds */
   4.298 +	if(is_red(tree->right)) {
   4.299 +		tree = rot_left(tree);
   4.300 +	}
   4.301 +	/* fix two reds in a row */
   4.302 +	if(is_red(tree->left) && is_red(tree->left->left)) {
   4.303 +		tree = rot_right(tree);
   4.304 +	}
   4.305 +
   4.306 +	/* if 4-node, split it by color inversion */
   4.307 +	if(is_red(tree->left) && is_red(tree->right)) {
   4.308 +		color_flip(tree);
   4.309 +	}
   4.310 +
   4.311 +	return tree;
   4.312 +}
   4.313 +
   4.314 +static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key)
   4.315 +{
   4.316 +	int cmp;
   4.317 +
   4.318 +	if(!tree) {
   4.319 +		return 0;
   4.320 +	}
   4.321 +
   4.322 +	cmp = rb->cmp(key, tree->key);
   4.323 +
   4.324 +	if(cmp < 0) {
   4.325 +		if(!is_red(tree->left) && !is_red(tree->left->left)) {
   4.326 +			tree = move_red_left(tree);
   4.327 +		}
   4.328 +		tree->left = delete(rb, tree->left, key);
   4.329 +	} else {
   4.330 +		/* need reds on the right */
   4.331 +		if(is_red(tree->left)) {
   4.332 +			tree = rot_right(tree);
   4.333 +		}
   4.334 +
   4.335 +		/* found it at the bottom (XXX what certifies left is null?) */
   4.336 +		if(cmp == 0 && !tree->right) {
   4.337 +			if(rb->del) {
   4.338 +				rb->del(tree, rb->del_cls);
   4.339 +			}
   4.340 +			rb->free(tree);
   4.341 +			return 0;
   4.342 +		}
   4.343 +
   4.344 +		if(!is_red(tree->right) && !is_red(tree->right->left)) {
   4.345 +			tree = move_red_left(tree);
   4.346 +		}
   4.347 +
   4.348 +		if(key == tree->key) {
   4.349 +			struct rbnode *rmin = find_min(tree->right);
   4.350 +			tree->key = rmin->key;
   4.351 +			tree->data = rmin->data;
   4.352 +			tree->right = del_min(rb, tree->right);
   4.353 +		} else {
   4.354 +			tree->right = delete(rb, tree->right, key);
   4.355 +		}
   4.356 +	}
   4.357 +
   4.358 +	return fix_up(tree);
   4.359 +}
   4.360 +
   4.361 +/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key)
   4.362 +{
   4.363 +	int cmp;
   4.364 +
   4.365 +	if(!node)
   4.366 +		return 0;
   4.367 +
   4.368 +	if((cmp = rb->cmp(key, node->key)) == 0) {
   4.369 +		return node;
   4.370 +	}
   4.371 +	return find(rb, cmp < 0 ? node->left : node->right, key);
   4.372 +}*/
   4.373 +
   4.374 +static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls)
   4.375 +{
   4.376 +	if(!node)
   4.377 +		return;
   4.378 +
   4.379 +	traverse(node->left, func, cls);
   4.380 +	func(node, cls);
   4.381 +	traverse(node->right, func, cls);
   4.382 +}
   4.383 +
   4.384 +/* helpers */
   4.385 +
   4.386 +static int is_red(struct rbnode *tree)
   4.387 +{
   4.388 +	return tree && tree->red;
   4.389 +}
   4.390 +
   4.391 +static void color_flip(struct rbnode *tree)
   4.392 +{
   4.393 +	tree->red = !tree->red;
   4.394 +	tree->left->red = !tree->left->red;
   4.395 +	tree->right->red = !tree->right->red;
   4.396 +}
   4.397 +
   4.398 +static struct rbnode *rot_left(struct rbnode *a)
   4.399 +{
   4.400 +	struct rbnode *b = a->right;
   4.401 +	a->right = b->left;
   4.402 +	b->left = a;
   4.403 +	b->red = a->red;
   4.404 +	a->red = 1;
   4.405 +	return b;
   4.406 +}
   4.407 +
   4.408 +static struct rbnode *rot_right(struct rbnode *a)
   4.409 +{
   4.410 +	struct rbnode *b = a->left;
   4.411 +	a->left = b->right;
   4.412 +	b->right = a;
   4.413 +	b->red = a->red;
   4.414 +	a->red = 1;
   4.415 +	return b;
   4.416 +}
   4.417 +
   4.418 +static struct rbnode *find_min(struct rbnode *tree)
   4.419 +{
   4.420 +	struct rbnode *node;
   4.421 +
   4.422 +	if(!tree)
   4.423 +		return 0;
   4.424 +
   4.425 +	while(node->left) {
   4.426 +		node = node->left;
   4.427 +	}
   4.428 +	return node;
   4.429 +}
   4.430 +
   4.431 +static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree)
   4.432 +{
   4.433 +	if(!tree->left) {
   4.434 +		if(rb->del) {
   4.435 +			rb->del(tree->left, rb->del_cls);
   4.436 +		}
   4.437 +		rb->free(tree->left);
   4.438 +		return 0;
   4.439 +	}
   4.440 +
   4.441 +	/* make sure we've got red (3/4-nodes) at the left side so we can delete at the bottom */
   4.442 +	if(!is_red(tree->left) && !is_red(tree->left->left)) {
   4.443 +		tree = move_red_left(tree);
   4.444 +	}
   4.445 +	tree->left = del_min(rb, tree->left);
   4.446 +
   4.447 +	/* fix right-reds, red-reds, and split 4-nodes on the way up */
   4.448 +	return fix_up(tree);
   4.449 +}
   4.450 +
   4.451 +#if 0
   4.452 +/* push a red link on this node to the right */
   4.453 +static struct rbnode *move_red_right(struct rbnode *tree)
   4.454 +{
   4.455 +	/* flipping it makes both children go red, so we have a red to the right */
   4.456 +	color_flip(tree);
   4.457 +
   4.458 +	/* if after the flip we've got a red-red situation to the left, fix it */
   4.459 +	if(is_red(tree->left->left)) {
   4.460 +		tree = rot_right(tree);
   4.461 +		color_flip(tree);
   4.462 +	}
   4.463 +	return tree;
   4.464 +}
   4.465 +#endif
   4.466 +
   4.467 +/* push a red link on this node to the left */
   4.468 +static struct rbnode *move_red_left(struct rbnode *tree)
   4.469 +{
   4.470 +	/* flipping it makes both children go red, so we have a red to the left */
   4.471 +	color_flip(tree);
   4.472 +
   4.473 +	/* if after the flip we've got a red-red on the right-left, fix it */
   4.474 +	if(is_red(tree->right->left)) {
   4.475 +		tree->right = rot_right(tree->right);
   4.476 +		tree = rot_left(tree);
   4.477 +		color_flip(tree);
   4.478 +	}
   4.479 +	return tree;
   4.480 +}
   4.481 +
   4.482 +static struct rbnode *fix_up(struct rbnode *tree)
   4.483 +{
   4.484 +	/* fix right-leaning */
   4.485 +	if(is_red(tree->right)) {
   4.486 +		tree = rot_left(tree);
   4.487 +	}
   4.488 +	/* change invalid red-red pairs into a proper 4-node */
   4.489 +	if(is_red(tree->left) && is_red(tree->left->left)) {
   4.490 +		tree = rot_right(tree);
   4.491 +	}
   4.492 +	/* split 4-nodes */
   4.493 +	if(is_red(tree->left) && is_red(tree->right)) {
   4.494 +		color_flip(tree);
   4.495 +	}
   4.496 +	return tree;
   4.497 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/rbtree.h	Wed Feb 12 22:05:28 2014 +0200
     5.3 @@ -0,0 +1,71 @@
     5.4 +#ifndef RBTREE_H_
     5.5 +#define RBTREE_H_
     5.6 +
     5.7 +struct rbtree;
     5.8 +
     5.9 +
    5.10 +struct rbnode {
    5.11 +	void *key, *data;
    5.12 +	int red;
    5.13 +	struct rbnode *left, *right;
    5.14 +	struct rbnode *next;	/* for iterator stack */
    5.15 +};
    5.16 +
    5.17 +
    5.18 +typedef void *(*rb_alloc_func_t)(size_t);
    5.19 +typedef void (*rb_free_func_t)(void*);
    5.20 +
    5.21 +typedef int (*rb_cmp_func_t)(const void*, const void*);
    5.22 +typedef void (*rb_del_func_t)(struct rbnode*, void*);
    5.23 +
    5.24 +#define RB_KEY_ADDR		(rb_cmp_func_t)(0)
    5.25 +#define RB_KEY_INT		(rb_cmp_func_t)(1)
    5.26 +#define RB_KEY_STRING	(rb_cmp_func_t)(3)
    5.27 +
    5.28 +
    5.29 +#ifdef __cplusplus
    5.30 +extern "C" {
    5.31 +#endif
    5.32 +
    5.33 +struct rbtree *rb_create(rb_cmp_func_t cmp_func);
    5.34 +void rb_free(struct rbtree *rb);
    5.35 +
    5.36 +int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func);
    5.37 +void rb_destroy(struct rbtree *rb);
    5.38 +
    5.39 +void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free);
    5.40 +void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func);
    5.41 +void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls);
    5.42 +/* TODO add user deep copy function */
    5.43 +
    5.44 +void rb_clear(struct rbtree *rb);
    5.45 +int rb_copy(struct rbtree *dest, struct rbtree *src);
    5.46 +
    5.47 +int rb_size(struct rbtree *rb);
    5.48 +
    5.49 +int rb_insert(struct rbtree *rb, void *key, void *data);
    5.50 +int rb_inserti(struct rbtree *rb, int key, void *data);
    5.51 +
    5.52 +int rb_delete(struct rbtree *rb, void *key);
    5.53 +int rb_deletei(struct rbtree *rb, int key);
    5.54 +
    5.55 +void *rb_find(struct rbtree *rb, void *key);
    5.56 +void *rb_findi(struct rbtree *rb, int key);
    5.57 +
    5.58 +void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls);
    5.59 +
    5.60 +struct rbnode *rb_root(struct rbtree *rb);
    5.61 +
    5.62 +void rb_begin(struct rbtree *rb);
    5.63 +struct rbnode *rb_next(struct rbtree *rb);
    5.64 +
    5.65 +void *rb_node_key(struct rbnode *node);
    5.66 +int rb_node_keyi(struct rbnode *node);
    5.67 +void *rb_node_data(struct rbnode *node);
    5.68 +
    5.69 +#ifdef __cplusplus
    5.70 +}
    5.71 +#endif
    5.72 +
    5.73 +
    5.74 +#endif	/* RBTREE_H_ */
     6.1 --- a/src/resman.c	Wed Feb 12 16:02:12 2014 +0200
     6.2 +++ b/src/resman.c	Wed Feb 12 22:05:28 2014 +0200
     6.3 @@ -5,8 +5,15 @@
     6.4  #include <pthread.h>
     6.5  #include "resman.h"
     6.6  #include "dynarr.h"
     6.7 +#include "rbtree.h"
     6.8  #include "threadpool.h"
     6.9  
    6.10 +#ifdef __linux__
    6.11 +#include <unistd.h>
    6.12 +#include <fcntl.h>
    6.13 +#include <sys/inotify.h>
    6.14 +#endif
    6.15 +
    6.16  struct resource {
    6.17  	int id;
    6.18  	char *name;
    6.19 @@ -15,7 +22,17 @@
    6.20  
    6.21  	int done_pending;
    6.22  	int delete_pending;
    6.23 -	pthread_mutex_t done_lock;
    6.24 +	pthread_mutex_t lock;
    6.25 +
    6.26 +	int num_loads;		/* number of loads up to now */
    6.27 +
    6.28 +	/* file change monitoring */
    6.29 +#ifdef __WIN32__
    6.30 +	HANDLE nhandle;
    6.31 +#endif
    6.32 +#ifdef __linux__
    6.33 +	int nfd;
    6.34 +#endif
    6.35  };
    6.36  
    6.37  struct resman {
    6.38 @@ -31,6 +48,13 @@
    6.39  	void *load_func_cls;
    6.40  	void *done_func_cls;
    6.41  	void *destroy_func_cls;
    6.42 +
    6.43 +	/* file change monitoring */
    6.44 +	struct rbtree *nresmap;
    6.45 +	struct rbtree *modset;
    6.46 +#ifdef __linux__
    6.47 +	int inotify_fd;
    6.48 +#endif
    6.49  };
    6.50  
    6.51  
    6.52 @@ -39,6 +63,15 @@
    6.53  static void remove_resource(struct resman *rman, int idx);
    6.54  static void work_func(void *data, void *cls);
    6.55  
    6.56 +/* file modification watching */
    6.57 +static int init_file_monitor(struct resman *rman);
    6.58 +static void destroy_file_monitor(struct resman *rman);
    6.59 +static int start_watch(struct resman *rman, struct resource *res);
    6.60 +static void stop_watch(struct resman *rman, struct resource *res);
    6.61 +static void check_watch(struct resman *rman);
    6.62 +static void reload_modified(struct rbnode *node, void *cls);
    6.63 +
    6.64 +
    6.65  struct resman *resman_create(void)
    6.66  {
    6.67  	struct resman *rman = malloc(sizeof *rman);
    6.68 @@ -66,6 +99,10 @@
    6.69  		num_threads = atoi(env);
    6.70  	}
    6.71  
    6.72 +	if(init_file_monitor(rman) == -1) {
    6.73 +		return -1;
    6.74 +	}
    6.75 +
    6.76  	if(!(rman->tpool = tpool_create(num_threads))) {
    6.77  		return -1;
    6.78  	}
    6.79 @@ -96,6 +133,8 @@
    6.80  
    6.81  	tpool_free(rman->tpool);
    6.82  
    6.83 +	destroy_file_monitor(rman);
    6.84 +
    6.85  	pthread_mutex_destroy(&rman->lock);
    6.86  }
    6.87  
    6.88 @@ -156,6 +195,10 @@
    6.89  	}
    6.90  
    6.91  
    6.92 +	/* then check for modified files */
    6.93 +	check_watch(rman);
    6.94 +
    6.95 +
    6.96  	if(!rman->done_func) {
    6.97  		return 0;	/* no done callback; there's no point in checking anything */
    6.98  	}
    6.99 @@ -166,21 +209,29 @@
   6.100  			continue;
   6.101  		}
   6.102  
   6.103 -		pthread_mutex_lock(&res->done_lock);
   6.104 +		pthread_mutex_lock(&res->lock);
   6.105  		if(!res->done_pending) {
   6.106 -			pthread_mutex_unlock(&res->done_lock);
   6.107 +			pthread_mutex_unlock(&res->lock);
   6.108  			continue;
   6.109  		}
   6.110  
   6.111  		/* so a done callback *is* pending... */
   6.112  		res->done_pending = 0;
   6.113  		if(rman->done_func(i, rman->done_func_cls) == -1) {
   6.114 -			/* done-func returned -1, so let's remove the resource */
   6.115 -			pthread_mutex_unlock(&res->done_lock);
   6.116 -			remove_resource(rman, i);
   6.117 -			continue;
   6.118 +			/* done-func returned -1, so let's remove the resource
   6.119 +			 * but only if this was the first load. Otherwise keep it
   6.120 +			 * around in case it gets valid again...
   6.121 +			 */
   6.122 +			if(res->num_loads == 0) {
   6.123 +				pthread_mutex_unlock(&res->lock);
   6.124 +				remove_resource(rman, i);
   6.125 +				continue;
   6.126 +			}
   6.127  		}
   6.128 -		pthread_mutex_unlock(&res->done_lock);
   6.129 +		res->num_loads++;
   6.130 +
   6.131 +		start_watch(rman, res);	/* start watching the file for modifications */
   6.132 +		pthread_mutex_unlock(&res->lock);
   6.133  	}
   6.134  	return 0;
   6.135  }
   6.136 @@ -216,6 +267,14 @@
   6.137  	return -1;
   6.138  }
   6.139  
   6.140 +int resman_get_res_load_count(struct resman *rman, int res_id)
   6.141 +{
   6.142 +	if(res_id >= 0 && res_id < dynarr_size(rman->res)) {
   6.143 +		return rman->res[res_id]->num_loads;
   6.144 +	}
   6.145 +	return -1;
   6.146 +}
   6.147 +
   6.148  static int find_resource(struct resman *rman, const char *fname)
   6.149  {
   6.150  	int i, sz = dynarr_size(rman->res);
   6.151 @@ -243,8 +302,7 @@
   6.152  	res->name = strdup(fname);
   6.153  	assert(res->name);
   6.154  	res->data = data;
   6.155 -	pthread_mutex_init(&res->done_lock, 0);
   6.156 -
   6.157 +	pthread_mutex_init(&res->lock, 0);
   6.158  
   6.159  	/* check to see if there's an emtpy (previously erased) slot */
   6.160  	for(i=0; i<size; i++) {
   6.161 @@ -278,11 +336,13 @@
   6.162  /* remove a resource and leave the pointer null to reuse the slot */
   6.163  static void remove_resource(struct resman *rman, int idx)
   6.164  {
   6.165 +	stop_watch(rman, rman->res[idx]);
   6.166 +
   6.167  	if(rman->destroy_func) {
   6.168  		rman->destroy_func(idx, rman->destroy_func_cls);
   6.169  	}
   6.170  
   6.171 -	pthread_mutex_destroy(&rman->res[idx]->done_lock);
   6.172 +	pthread_mutex_destroy(&rman->res[idx]->lock);
   6.173  
   6.174  	free(rman->res[idx]);
   6.175  	rman->res[idx] = 0;
   6.176 @@ -296,16 +356,124 @@
   6.177  	struct resource *res = data;
   6.178  	struct resman *rman = cls;
   6.179  
   6.180 +	pthread_mutex_lock(&res->lock);
   6.181 +
   6.182  	res->result = rman->load_func(res->name, res->id, rman->load_func_cls);
   6.183 -	if(res->result == -1 && !rman->done_func) {
   6.184 -		/* if there's no done function and we got an error, mark this
   6.185 -		 * resource for deletion in the caller context
   6.186 -		 */
   6.187 -		res->delete_pending = 1;
   6.188 +	if(!rman->done_func) {
   6.189 +		if(res->result == -1) {
   6.190 +			/* if there's no done function and we got an error, mark this
   6.191 +			 * resource for deletion in the caller context. But only if this
   6.192 +			 * is the first load of this resource.
   6.193 +			 */
   6.194 +			if(res->num_loads == 0) {
   6.195 +				res->delete_pending = 1;
   6.196 +			}
   6.197 +		} else {
   6.198 +			/* succeded, start a watch */
   6.199 +			if(res->nfd <= 0) {
   6.200 +				start_watch(rman, res);
   6.201 +			}
   6.202 +		}
   6.203 +	} else {
   6.204 +		/* if we have a done_func, mark this resource as done */
   6.205 +		res->done_pending = 1;
   6.206 +	}
   6.207 +	pthread_mutex_unlock(&res->lock);
   6.208 +}
   6.209 +
   6.210 +static int init_file_monitor(struct resman *rman)
   6.211 +{
   6.212 +	int fd;
   6.213 +
   6.214 +	if((fd = inotify_init()) == -1) {
   6.215 +		return -1;
   6.216 +	}
   6.217 +	/* set non-blocking flag, to allow polling by reading */
   6.218 +	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
   6.219 +	rman->inotify_fd = fd;
   6.220 +
   6.221 +	/* create the fd->resource map */
   6.222 +	rman->nresmap = rb_create(RB_KEY_INT);
   6.223 +	/* create the modified set */
   6.224 +	rman->modset = rb_create(RB_KEY_INT);
   6.225 +	return 0;
   6.226 +}
   6.227 +
   6.228 +static void destroy_file_monitor(struct resman *rman)
   6.229 +{
   6.230 +	rb_free(rman->nresmap);
   6.231 +	rb_free(rman->modset);
   6.232 +
   6.233 +	if(rman->inotify_fd >= 0) {
   6.234 +		close(rman->inotify_fd);
   6.235 +		rman->inotify_fd = -1;
   6.236 +	}
   6.237 +}
   6.238 +
   6.239 +static int start_watch(struct resman *rman, struct resource *res)
   6.240 +{
   6.241 +	int fd;
   6.242 +
   6.243 +	if((fd = inotify_add_watch(rman->inotify_fd, res->name, IN_MODIFY)) == -1) {
   6.244 +		return -1;
   6.245 +	}
   6.246 +	printf("started watching file \"%s\" for modification (fd %d)\n", res->name, fd);
   6.247 +	rb_inserti(rman->nresmap, fd, res);
   6.248 +
   6.249 +	res->nfd = fd;
   6.250 +	return 0;
   6.251 +}
   6.252 +
   6.253 +static void stop_watch(struct resman *rman, struct resource *res)
   6.254 +{
   6.255 +	if(res->nfd > 0) {
   6.256 +		rb_deletei(rman->nresmap, res->nfd);
   6.257 +		inotify_rm_watch(rman->inotify_fd, res->nfd);
   6.258 +	}
   6.259 +}
   6.260 +
   6.261 +static void check_watch(struct resman *rman)
   6.262 +{
   6.263 +	char buf[512];
   6.264 +	struct inotify_event *ev;
   6.265 +	int sz, evsize;
   6.266 +
   6.267 +	while((sz = read(rman->inotify_fd, buf, sizeof buf)) > 0) {
   6.268 +		ev = (struct inotify_event*)buf;
   6.269 +		while(sz > 0) {
   6.270 +			if(ev->mask & IN_MODIFY) {
   6.271 +				/* add the file descriptor to the modified set */
   6.272 +				rb_inserti(rman->modset, ev->wd, 0);
   6.273 +			}
   6.274 +
   6.275 +			evsize = sizeof *ev + ev->len;
   6.276 +			sz -= evsize;
   6.277 +			ev += evsize;
   6.278 +		}
   6.279 +	}
   6.280 +
   6.281 +	/* for each item in the modified set, start a new job to reload it */
   6.282 +	rb_foreach(rman->modset, reload_modified, rman);
   6.283 +	rb_clear(rman->modset);
   6.284 +}
   6.285 +
   6.286 +/* this is called for each item in the modified set (see above) */
   6.287 +static void reload_modified(struct rbnode *node, void *cls)
   6.288 +{
   6.289 +	int watch_fd;
   6.290 +	struct resource *res;
   6.291 +	struct resman *rman = cls;
   6.292 +
   6.293 +	watch_fd = rb_node_keyi(node);
   6.294 +
   6.295 +	if(!(res = rb_findi(rman->nresmap, watch_fd))) {
   6.296 +		fprintf(stderr, "%s: can't find resource for watch descriptor: %d\n",
   6.297 +				__FUNCTION__, watch_fd);
   6.298  		return;
   6.299  	}
   6.300 +	assert(watch_fd == res->nfd);
   6.301  
   6.302 -	pthread_mutex_lock(&res->done_lock);
   6.303 -	res->done_pending = 1;
   6.304 -	pthread_mutex_unlock(&res->done_lock);
   6.305 +	printf("file \"%s\" modified (fd %d)\n", res->name, rb_node_keyi(node));
   6.306 +
   6.307 +	tpool_add_work(rman->tpool, res);
   6.308  }
     7.1 --- a/src/resman.h	Wed Feb 12 16:02:12 2014 +0200
     7.2 +++ b/src/resman.h	Wed Feb 12 22:05:28 2014 +0200
     7.3 @@ -37,6 +37,8 @@
     7.4  
     7.5  int resman_get_res_result(struct resman *rman, int res_id);
     7.6  
     7.7 +int resman_get_res_load_count(struct resman *rman, int res_id);
     7.8 +
     7.9  #ifdef __cplusplus
    7.10  }
    7.11  #endif
     8.1 --- a/src/threadpool.c	Wed Feb 12 16:02:12 2014 +0200
     8.2 +++ b/src/threadpool.c	Wed Feb 12 22:05:28 2014 +0200
     8.3 @@ -164,23 +164,28 @@
     8.4  		}
     8.5  	}
     8.6  
     8.7 -	pthread_mutex_lock(&tpool->work_lock);
     8.8  	for(;;) {
     8.9 +		int job_id;
    8.10 +		void *data;
    8.11 +
    8.12 +		pthread_mutex_lock(&tpool->work_lock);
    8.13  		/* while there aren't any work items to do go to sleep on the condvar */
    8.14 -		pthread_cond_wait(&tpool->work_cond, &tpool->work_lock);
    8.15 -		if(!tpool->work_list) {
    8.16 -			continue;	/* spurious wakeup, go back to sleep */
    8.17 +		while(!tpool->work_list) {
    8.18 +			pthread_cond_wait(&tpool->work_cond, &tpool->work_lock);
    8.19  		}
    8.20  
    8.21  		job = tpool->work_list;
    8.22  		tpool->work_list = tpool->work_list->next;
    8.23  
    8.24 -		printf("TPOOL: worker %d start job: %d\n", tidx, job->id);
    8.25 -		tpool->work_func(job->data, tpool->cls);
    8.26 -		printf("TPOOL: worker %d completed job: %d\n", tidx, job->id);
    8.27 +		job_id = job->id;
    8.28 +		data = job->data;
    8.29  		free_node(job);
    8.30 +		pthread_mutex_unlock(&tpool->work_lock);
    8.31 +
    8.32 +		printf("TPOOL: worker %d start job: %d\n", tidx, job_id);
    8.33 +		tpool->work_func(data, tpool->cls);
    8.34 +		printf("TPOOL: worker %d completed job: %d\n", tidx, job_id);
    8.35  	}
    8.36 -	pthread_mutex_unlock(&tpool->work_lock);
    8.37  	return 0;
    8.38  }
    8.39