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