libresman

diff src/filewatch_win32.c @ 23:f8e5a1491275

win32 file change notification attempt1 (failed)
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 13 Feb 2014 13:17:07 +0200
parents 174ddb6bf92a
children ce04fa12afdd
line diff
     1.1 --- a/src/filewatch_win32.c	Wed Feb 12 22:32:30 2014 +0200
     1.2 +++ b/src/filewatch_win32.c	Thu Feb 13 13:17:07 2014 +0200
     1.3 @@ -2,110 +2,171 @@
     1.4  #ifdef WIN32
     1.5  #include <stdio.h>
     1.6  #include <assert.h>
     1.7 -#include <unistd.h>
     1.8 -#include <fcntl.h>
     1.9 -#include <sys/inotify.h>
    1.10 +#include <malloc.h>
    1.11  #include "filewatch.h"
    1.12  #include "resman.h"
    1.13  #include "resman_impl.h"
    1.14 +#include "dynarr.h"
    1.15 +
    1.16 +struct watch_dir {
    1.17 +	HANDLE handle;
    1.18 +	int nref;
    1.19 +};
    1.20  
    1.21  static void reload_modified(struct rbnode *node, void *cls);
    1.22  
    1.23  int resman_init_file_monitor(struct resman *rman)
    1.24  {
    1.25 -	int fd;
    1.26 -
    1.27 -	if((fd = inotify_init()) == -1) {
    1.28 +	if(!(rman->watch_handles = dynarr_alloc(0, sizeof *rman->watch_handles))) {
    1.29  		return -1;
    1.30  	}
    1.31 -	/* set non-blocking flag, to allow polling by reading */
    1.32 -	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
    1.33 -	rman->inotify_fd = fd;
    1.34  
    1.35 -	/* create the fd->resource map */
    1.36 -	rman->nresmap = rb_create(RB_KEY_INT);
    1.37 -	/* create the modified set */
    1.38 -	rman->modset = rb_create(RB_KEY_INT);
    1.39 +	/* create the handle->resource map */
    1.40 +	rman->nresmap = rb_create(RB_KEY_ADDR);
    1.41 +	/* create the watched dirs set */
    1.42 +	rman->watchdirs = rb_create(RB_KEY_STRING);
    1.43  	return 0;
    1.44  }
    1.45  
    1.46  void resman_destroy_file_monitor(struct resman *rman)
    1.47  {
    1.48 +	dynarr_free(rman->watch_handles);
    1.49 +
    1.50  	rb_free(rman->nresmap);
    1.51 -	rb_free(rman->modset);
    1.52 -
    1.53 -	if(rman->inotify_fd >= 0) {
    1.54 -		close(rman->inotify_fd);
    1.55 -		rman->inotify_fd = -1;
    1.56 -	}
    1.57 +	rb_free(rman->watchdirs);
    1.58  }
    1.59  
    1.60  int resman_start_watch(struct resman *rman, struct resource *res)
    1.61  {
    1.62 -	int fd;
    1.63 +	char *path;
    1.64 +	HANDLE handle;
    1.65 +	struct watch_dir *wdir;
    1.66  
    1.67 -	if((fd = inotify_add_watch(rman->inotify_fd, res->name, IN_MODIFY)) == -1) {
    1.68 -		return -1;
    1.69 +	/* construct an absolute path for the directory containing this file */
    1.70 +	path = res->name;
    1.71 +	if(path[0] != '/' && path[1] != ':') {	/* not an absolute path */
    1.72 +		char *src, *dest, *lastslash;
    1.73 +		int cwdsz = GetCurrentDirectory(0, 0);
    1.74 +		int pathsz = strlen(path) + cwdsz + 1;
    1.75 +
    1.76 +		path = malloc(pathsz + 1);
    1.77 +		GetCurrentDirectory(pathsz, path);
    1.78 +
    1.79 +		/* now copy the rest of the path, until the last slash, while converting path separators */
    1.80 +		src = res->name;
    1.81 +		dest = path + strlen(path);
    1.82 +
    1.83 +		lastslash = dest;
    1.84 +		*dest++ = '\\';
    1.85 +		while(*src) {
    1.86 +			if(src[-1] == '\\') {
    1.87 +				/* skip any /./ parts of the path */
    1.88 +				if(src[0] == '.' && (src[1] == '/' || src[1] == '\\')) {
    1.89 +					src += 2;
    1.90 +					continue;
    1.91 +				}
    1.92 +				/* normalize any /../ parts of the path */
    1.93 +				if(src[0] == '.' && src[1] == '.' && (src[2] == '/' || src[2] == '\\')) {
    1.94 +					src += 3;
    1.95 +					dest = strrchr(src - 2, '\\');
    1.96 +					assert(dest);
    1.97 +					dest++;
    1.98 +					continue;
    1.99 +				}
   1.100 +			}
   1.101 +
   1.102 +			if(*src == '/' || *src == '\\') {
   1.103 +				lastslash = dest;
   1.104 +				*dest++ = '\\';
   1.105 +				src++;
   1.106 +			} else {
   1.107 +				*dest++ = *src++;
   1.108 +			}
   1.109 +		}
   1.110 +
   1.111 +		*lastslash = 0;
   1.112  	}
   1.113 -	printf("started watching file \"%s\" for modification (fd %d)\n", res->name, fd);
   1.114 -	rb_inserti(rman->nresmap, fd, res);
   1.115  
   1.116 -	res->nfd = fd;
   1.117 +	/* check to see if we already have a watch handle for this directory */
   1.118 +	if((wdir = rb_find(rman->watchdirs, path))) {
   1.119 +		handle = wdir->handle;
   1.120 +		wdir->nref++;
   1.121 +	} else {
   1.122 +		if(!(wdir = malloc(sizeof *wdir))) {
   1.123 +			perror("failed to allocate watchdir");
   1.124 +			free(path);
   1.125 +			return -1;
   1.126 +		}
   1.127 +
   1.128 +		/* otherwise start a new notification */
   1.129 +		if((handle = FindFirstChangeNotification(path, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE)) == INVALID_HANDLE_VALUE) {
   1.130 +			unsigned int err = GetLastError();
   1.131 +			fprintf(stderr, "failed to watch %s for modification (error: %u)\n", path, err);
   1.132 +			free(wdir);
   1.133 +			free(path);
   1.134 +			return -1;
   1.135 +		}
   1.136 +		wdir->handle = handle;
   1.137 +		wdir->nref = 1;
   1.138 +
   1.139 +		rb_insert(rman->watchdirs, path, wdir);
   1.140 +		dynarr_push(rman->watch_handles, &handle);
   1.141 +	}
   1.142 +
   1.143 +	rb_insert(rman->nresmap, handle, res);
   1.144 +	res->nhandle = handle;
   1.145 +	res->watch_path = path;
   1.146  	return 0;
   1.147  }
   1.148  
   1.149  void resman_stop_watch(struct resman *rman, struct resource *res)
   1.150  {
   1.151 -	if(res->nfd > 0) {
   1.152 -		rb_deletei(rman->nresmap, res->nfd);
   1.153 -		inotify_rm_watch(rman->inotify_fd, res->nfd);
   1.154 +	int i, sz;
   1.155 +
   1.156 +	if(res->nhandle) {
   1.157 +		struct watch_dir *wdir = rb_find(rman->watchdirs, res->watch_path);
   1.158 +		if(wdir) {
   1.159 +			if(--wdir->nref <= 0) {
   1.160 +				FindCloseChangeNotification(res->nhandle);
   1.161 +
   1.162 +				/* find the handle in the watch_handles array and remove it */
   1.163 +				sz = dynarr_size(rman->watch_handles);
   1.164 +				for(i=0; i<sz; i++) {
   1.165 +					if(rman->watch_handles[i] == res->nhandle) {
   1.166 +						/* swap the end for it and pop */
   1.167 +						rman->watch_handles[i] = rman->watch_handles[sz - 1];
   1.168 +						rman->watch_handles[sz - 1] = 0;
   1.169 +						dynarr_pop(rman->watch_handles);
   1.170 +						break;
   1.171 +					}
   1.172 +				}
   1.173 +			}
   1.174 +			free(wdir);
   1.175 +		}
   1.176 +
   1.177 +		rb_delete(rman->nresmap, res->nhandle);
   1.178 +		res->nhandle = 0;
   1.179  	}
   1.180  }
   1.181  
   1.182  void resman_check_watch(struct resman *rman)
   1.183  {
   1.184 -	char buf[512];
   1.185 -	struct inotify_event *ev;
   1.186 -	int sz, evsize;
   1.187 +	unsigned int num_handles = dynarr_size(rman->watch_handles);
   1.188 +	for(;;) {
   1.189 +		struct resource *res;
   1.190 +		unsigned int idx = WaitForMultipleObjects(num_handles, rman->watch_handles, FALSE, 0);
   1.191 +		if(idx < WAIT_OBJECT_0 || idx >= WAIT_OBJECT_0 + num_handles) {
   1.192 +			break;
   1.193 +		}
   1.194  
   1.195 -	while((sz = read(rman->inotify_fd, buf, sizeof buf)) > 0) {
   1.196 -		ev = (struct inotify_event*)buf;
   1.197 -		while(sz > 0) {
   1.198 -			if(ev->mask & IN_MODIFY) {
   1.199 -				/* add the file descriptor to the modified set */
   1.200 -				rb_inserti(rman->modset, ev->wd, 0);
   1.201 -			}
   1.202 +		if(!(res = rb_find(rman->nresmap, rman->watch_handles[idx]))) {
   1.203 +			fprintf(stderr, "got modification event from unknown resource!\n");
   1.204 +			continue;
   1.205 +		}
   1.206  
   1.207 -			evsize = sizeof *ev + ev->len;
   1.208 -			sz -= evsize;
   1.209 -			ev += evsize;
   1.210 -		}
   1.211 +		printf("file \"%s\" modified\n", res->name);
   1.212 +		tpool_add_work(rman->tpool, res);
   1.213  	}
   1.214 -
   1.215 -	/* for each item in the modified set, start a new job to reload it */
   1.216 -	rb_foreach(rman->modset, reload_modified, rman);
   1.217 -	rb_clear(rman->modset);
   1.218 -}
   1.219 -
   1.220 -/* this is called for each item in the modified set (see above) */
   1.221 -static void reload_modified(struct rbnode *node, void *cls)
   1.222 -{
   1.223 -	int watch_fd;
   1.224 -	struct resource *res;
   1.225 -	struct resman *rman = cls;
   1.226 -
   1.227 -	watch_fd = rb_node_keyi(node);
   1.228 -
   1.229 -	if(!(res = rb_findi(rman->nresmap, watch_fd))) {
   1.230 -		fprintf(stderr, "%s: can't find resource for watch descriptor: %d\n",
   1.231 -				__FUNCTION__, watch_fd);
   1.232 -		return;
   1.233 -	}
   1.234 -	assert(watch_fd == res->nfd);
   1.235 -
   1.236 -	printf("file \"%s\" modified (fd %d)\n", res->name, rb_node_keyi(node));
   1.237 -
   1.238 -	tpool_add_work(rman->tpool, res);
   1.239  }
   1.240  
   1.241  #else