pnotify.c

Go to the documentation of this file.
00001 /*              $Id: $          */
00002 
00003 /*
00004  * Copyright (c) 2007 Mark Heily <devel@heily.com>
00005  *
00006  * Permission to use, copy, modify, and distribute this software for any
00007  * purpose with or without fee is hereby granted, provided that the above
00008  * copyright notice and this permission notice appear in all copies.
00009  *
00010  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00011  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00012  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00013  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00014  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00015  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00016  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00017  */
00018 
00019 #include <assert.h>
00020 #include <err.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <stdbool.h>
00024 #include <stdint.h>
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <sys/param.h>
00029 #include <sys/stat.h>
00030 #include <sys/types.h>
00031 #include <sys/time.h>
00032 #include <unistd.h>
00033 
00034 #include "pnotify.h"
00035 #include "pnotify-internal.h"
00036 #include "queue.h"
00037 #include "thread.h"
00038 
00049 static pthread_key_t CTX_KEY;
00050 
00051 #define CTX_GET()       ((struct pnotify_ctx *) pthread_getspecific(CTX_KEY))
00052 #define CTX_SET(ctx)    (pthread_setspecific(CTX_KEY, ctx))
00053 
00054 
00055 /* Define the system-specific vtable.  */
00056 #if HAVE_KQUEUE
00057 const struct pnotify_vtable * const sys = &BSD_VTABLE;
00058 #else
00059 const struct pnotify_vtable * const sys = &LINUX_VTABLE;
00060 #endif
00061 
00063 LIST_HEAD(pnwatchhead, pn_watch) WATCH;
00064 pthread_mutex_t WATCH_MUTEX = PTHREAD_MUTEX_INITIALIZER;
00065 
00066 
00067 struct pn_watch *
00068 pn_get_watch_by_id(int wd)
00069 {
00070         struct pn_watch *watch;
00071         
00072         /* Find the matching watch structure */
00073         pthread_mutex_lock(&WATCH_MUTEX);
00074         LIST_FOREACH(watch, &WATCH, entries) {
00075                 if (watch->wd == wd) {
00076                         pthread_mutex_unlock(&WATCH_MUTEX);
00077                         return watch;
00078                 }
00079         }
00080         pthread_mutex_unlock(&WATCH_MUTEX);
00081 
00082         /* If no matching watch is found, return NULL */
00083         //warn("get_watch_by_id(): watch # %d not found", wd);
00084         return NULL;
00085 }
00086 
00087 
00088 void
00089 pnotify_init_once(void)
00090 {
00091         pthread_t tid;
00092 
00093         /* Initialize the TLS key */
00094         if (pthread_key_create(&CTX_KEY, NULL) != 0) 
00095                 errx(1, "error creating TLS key");
00096 
00097         /* Block all signals */
00098         pn_mask_signals();
00099 
00100         /* Create a dedicated signal handling thread */
00101         if (pthread_create( &tid, NULL, pn_signal_loop, NULL ) != 0)
00102                 errx(1, "pthread_create(3) failed");
00103 
00104         /* Create a dedicated timer thread */
00105         if (pthread_create( &tid, NULL, pn_timer_loop, NULL ) != 0)
00106                 errx(1, "pthread_create(3) failed");
00107 
00108         /* Initialize lists */
00109         LIST_INIT(&WATCH);
00110         pn_timer_init();
00111 
00112         /* Perform system-specific initialization */
00113         sys->init_once();
00114 }
00115 
00116 /* 
00117  * FIXME: 
00118  * need to call free() and pthread_mutex_destroy() in
00119  * the error handing paths.
00120  */
00121 struct pnotify_ctx *
00122 pnotify_init()
00123 {
00124         static pthread_once_t once = PTHREAD_ONCE_INIT;
00125         struct pnotify_ctx *ctx;
00126 
00127         /* Perform one-time initialization */
00128         pthread_once(&once, pnotify_init_once);
00129 
00130         /* Allocate a new context structure */
00131         if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
00132                 warn("calloc(3) failed");
00133                 return NULL;
00134         }
00135 
00136         /* Initialize the mutex */
00137         if (pthread_mutex_init(&ctx->mutex, NULL) != 0) {
00138                 warn("pthread_mutex_init(3) failed");
00139                 return NULL;
00140         }
00141 
00142         /* Initialize the counting semaphore */
00143         if (sem_init(&ctx->event_count, 0, 0) != 0) {
00144                 warn("sem_init(3) failed");
00145                 return NULL;
00146         }
00147                 
00148         TAILQ_INIT(&ctx->event);
00149 
00150         /* Set the global per-thread context variable */
00151         if (pthread_setspecific(CTX_KEY, ctx) != 0) {
00152                 warn("pthread_setspecific(3) failed");
00153                 return NULL;
00154         }
00155 
00156         /* Push the cleanup routine on the stack */
00157         //FIXME: macro error: pthread_cleanup_push(pnotify_free, ctx);
00158 
00159         return ctx;
00160 }
00161 
00162 
00163 int
00164 pnotify_add_watch(struct pnotify_watch *watch)
00165 {
00166         static int next_wd = 100;
00167         static pthread_mutex_t next_wd_mutex = PTHREAD_MUTEX_INITIALIZER;
00168         struct pn_watch *_watch;
00169         size_t len;
00170 
00171         /* Get the context */
00172         if (!watch->ctx)
00173                 watch->ctx = CTX_GET();
00174 
00175         /* Allocate a new entry */
00176         if ((_watch = malloc(sizeof(*_watch))) == NULL) {
00177                 warn("malloc error");
00178                 return -1;
00179         }
00180 
00181         /* Generate a new watch descriptor */
00182         if (watch->type == WATCH_SIGNAL) {
00183                 /* Special case: a signal's wd is the same as its signal number */
00184                 _watch->wd = watch->ident.signum;
00185         } else {
00186                 pthread_mutex_lock(&next_wd_mutex);
00187                 _watch->wd = next_wd++;
00188                 pthread_mutex_unlock(&next_wd_mutex);
00189                 if (_watch->wd < 0) {
00190                         warnx("watch descriptor overflow");
00191                         free(_watch);
00192                         return -1;
00193                 }
00194         }
00195 
00196         /* Copy the resource identifier */
00197         switch (watch->type) {
00198 
00199                 case WATCH_VNODE:
00200                         len = strlen(watch->ident.path);
00201                         if (len > PATH_MAX)
00202                                 return -1;
00203                         _watch->ident.path = malloc(len + 1);
00204                         /* TODO: malloc error handling */
00205                         (void) strncpy(_watch->ident.path, watch->ident.path, len);
00206                         break;
00207 
00208                 case WATCH_SIGNAL:
00209                 case WATCH_TIMER:
00210                 case WATCH_FD:
00211                         _watch->ident = watch->ident;
00212                         break;
00213 
00214                 default:
00215                         warn("invalid watch type = %d", watch->type);
00216                         return -1;
00217         }
00218 
00219         /* Copy the other watch fields */
00220         _watch->type = watch->type;
00221         _watch->mask = watch->mask;
00222         _watch->cb = watch->cb;
00223         _watch->arg = watch->arg;
00224         _watch->ctx = watch->ctx;
00225 
00226         /* Register the watch with the kernel */
00227         if (sys->add_watch(_watch) < 0) {
00228                 warn("adding watch failed");
00229                 //TODO: free(watch->ident.path);
00230                 free(_watch);
00231                 return -1;
00232         }
00233 
00234         /* Add the watch to the watchlist */
00235         pthread_mutex_lock(&WATCH_MUTEX);
00236         LIST_INSERT_HEAD(&WATCH, _watch, entries);
00237         pthread_mutex_unlock(&WATCH_MUTEX);
00238 
00239         dprintf("added watch: wd=%d mask=%d path=%s\n", 
00240                 watch->wd, watch->mask, watch->path);
00241 
00242         return _watch->wd;
00243 }
00244 
00245 
00246 int 
00247 pnotify_rm_watch(int wd)
00248 {
00249         struct pn_watch *watchp, *wtmp;
00250         int found = 0;
00251 
00252         pthread_mutex_lock(&WATCH_MUTEX);
00253 
00254         /* Find the matching watch structure(s) */
00255         LIST_FOREACH_SAFE(watchp, &WATCH, entries, wtmp) {
00256 
00257                 /* Remove the parent watch and it's children */
00258                 if ((watchp->wd == wd) || (watchp->parent_wd == wd)) {
00259                         if (sys->rm_watch(watchp) < 0)
00260                                 break;
00261                         switch (watchp->type) {
00262                                 case WATCH_TIMER: 
00263                                         (void) pn_rm_timer(watchp);
00264                                         break;
00265                                 default: 
00266                                         break;
00267                         }
00268                         LIST_REMOVE(watchp, entries);
00269                         free(watchp);
00270                         found++;
00271                 }
00272         }
00273 
00274         pthread_mutex_unlock(&WATCH_MUTEX);
00275 
00276         if (found == 0) {
00277                 warn("watch # %d not found", wd);
00278                 return -1;
00279         } else {
00280                 return 0;
00281         }
00282 }
00283 
00284 
00285 int
00286 pnotify_get_event(struct pnotify_event * evt, struct pnotify_ctx * ctx)
00287 {
00288         struct pnotify_event *evp;
00289 
00290         assert(evt);
00291 
00292         /* Get the context */
00293         if (!ctx)
00294                 ctx = CTX_GET();
00295 
00296         /* Wait for an event to be added to the queue */
00297         if (sem_wait(&ctx->event_count) != 0) {
00298                 warn("sem_wait(3) failed");
00299                 return -1;
00300         }
00301                 
00302 
00303         /* Shift the first element off of the pending event queue */
00304         mutex_lock(ctx);
00305         if (!TAILQ_EMPTY(&ctx->event)) {
00306                 evp = TAILQ_FIRST(&ctx->event);
00307                 TAILQ_REMOVE(&ctx->event, evp, entries);
00308                 mutex_unlock(ctx);
00309                 memcpy(evt, evp, sizeof(*evt));
00310                 free(evp);
00311                 return 0;
00312         } else {
00313                 warnx("spurious wakeup");
00314                 mutex_unlock(ctx);
00315                 return -1;
00316         }
00317 }
00318 
00319 
00320 int
00321 pnotify_print_event(struct pnotify_event * evt)
00322 {
00323         assert(evt);
00324 
00325         return printf("event: wd=%d mask=(%s%s%s%s%s) name=`%s'\n",
00326                       evt->watch->wd,
00327                       evt->mask & PN_ATTRIB ? "attrib," : "",
00328                       evt->mask & PN_CREATE ? "create," : "",
00329                       evt->mask & PN_DELETE ? "delete," : "",
00330                       evt->mask & PN_MODIFY ? "modify," : "",
00331                       evt->mask & PN_ERROR ? "error," : "",
00332                       evt->name);
00333 }
00334 
00335 
00336 void
00337 pnotify_dump(struct pnotify_ctx *ctx)
00338 {
00339         struct pnotify_event *evt;
00340 
00341         mutex_lock(ctx);
00342 
00343         printf("\npending events:\n");
00344         TAILQ_FOREACH(evt, &ctx->event, entries) {
00345                 printf("\t");
00346                 (void) pnotify_print_event(evt);
00347         }
00348         printf("/* end: pending events */\n");
00349 
00350         mutex_unlock(ctx);
00351 }
00352 
00353 
00354 void
00355 pnotify_free(struct pnotify_ctx *ctx)
00356 {
00357         struct pnotify_event *evt, *nxt;
00358 
00359         assert(ctx != NULL);
00360 
00361         mutex_lock(ctx);
00362 
00363 #if FIXME
00364         // need to scan the global watchlist
00365         
00366         /* Delete all watches */
00367         //struct pn_watch *watch;
00368         while (!LIST_EMPTY(&ctx->watch)) {
00369                 watch = LIST_FIRST(&ctx->watch);
00370                 if (pnotify_rm_watch(ctx, watch->wd) < 0) 
00371                         errx(1,"error removing watch");
00372         }
00373 #endif
00374 
00375         /* Delete all pending events */
00376         evt = TAILQ_FIRST(&ctx->event);
00377         while (evt != NULL) {
00378                 nxt = TAILQ_NEXT(evt, entries);
00379                 free(evt);
00380                 evt = nxt;
00381         }
00382 
00383         /* Destroy the semaphore and mutex */
00384         if (sem_destroy(&ctx->event_count) != 0) 
00385                 err(1, "sem_init(3) failed");
00386         if (pthread_mutex_destroy(&ctx->mutex) != 0) 
00387                 err(1, "pthread_mutex_destroy(3) failed");
00388 
00389         /* Perform system-specific cleanup */
00390         sys->cleanup();
00391 
00392         mutex_unlock(ctx);
00393         free(ctx);
00394 }
00395 
00396 
00397 /* -------- Convenience functions for pnotify_add_watch() ----------------- */
00398 
00399 int
00400 pnotify_watch_vnode(const char *path, int mask, void (*cb)(), void *arg)
00401 {
00402         struct pnotify_watch w;
00403 
00404         w.type = WATCH_VNODE;
00405         w.ident.path = (char *) path;
00406         w.mask = mask;
00407         w.cb = cb;
00408         w.arg = arg;
00409 
00410         return pnotify_add_watch(&w);
00411 }
00412 
00413 
00414 int
00415 pnotify_watch_fd(int fd, int mask, void (*cb)(), void *arg)
00416 {
00417         struct pnotify_watch w;
00418 
00419         w.type = WATCH_FD;
00420         w.ident.fd = fd;
00421         w.mask = mask;
00422         w.cb = cb;
00423         w.arg = arg;
00424 
00425         return pnotify_add_watch(&w);
00426 }
00427 
00428 int
00429 pnotify_set_timer(int interval, int mask, void (*cb)(), void *arg)
00430 {
00431         struct pnotify_watch w;
00432         int wd;
00433 
00434         /* Add the watch */
00435         w.type = WATCH_TIMER;
00436         w.ident.interval = interval;
00437         w.mask = mask;
00438         w.cb = cb;
00439         w.arg = arg;
00440         if ((wd = pnotify_add_watch(&w)) < 0) {
00441                 warnx("unable to add watch for timer");
00442                 return -1;
00443         }
00444 
00445         /* Set a timer */
00446         if (pn_add_timer(pn_get_watch_by_id(wd))) {
00447                 warnx("unable to add timer");
00448                 return -1;
00449         }
00450 
00451         return 0;
00452 }
00453 
00454 int
00455 pnotify_trap_signal(int signum, void (*cb)(), void *arg)
00456 {
00457         struct pnotify_watch w;
00458 
00459         /* Register the signal with the signal handling thread */
00460         if (pn_trap_signal(CTX_GET(), signum) != 0)
00461                 return -1;
00462 
00463         w.type = WATCH_SIGNAL;
00464         w.ident.signum = signum;
00465         w.mask = PN_SIGNAL;
00466         w.cb = cb;
00467         w.arg = arg;
00468 
00469         return pnotify_add_watch(&w);
00470 }
00471 
00472 int pnotify_call_function(int (*func)(), size_t nargs, ...)
00473 {
00474         abort(); 
00475 
00476         /* FIXME - TODO */
00477         
00478 }
00479 
00480 int
00481 pnotify_dispatch()
00482 {
00483         struct pnotify_event evt;
00484         //void (*fn)(union pn_resource_id, int, void *);
00485 
00486         for (;;) {
00487                 /* Wait for an event */
00488                 if (pnotify_get_event(&evt, NULL) != 0)
00489                         return -1;
00490 
00491                 /* Ignore events that have no callback defined */
00492                 if (!evt.watch->cb) 
00493                         continue;
00494                         
00495                 if (evt.watch->type == WATCH_VNODE) {
00496                         //FIXME: need to copy path to caller
00497                         //*(evt->watch->cb)(evt->, 
00498                 } else if (evt.watch->type == WATCH_TIMER) {
00499                          evt.watch->cb(evt.mask, evt.watch->arg);
00500                 } else {
00501                          evt.watch->cb(evt.watch->ident.fd, evt.mask, evt.watch->arg);
00502                 }
00503 
00504         }
00505         return 0;
00506 }
00507 
00508 
00509 void
00510 pn_event_add(struct pnotify_ctx *ctx, struct pnotify_event *evt)
00511 {
00512         /* Assign the event to a context */
00513         mutex_lock(ctx);
00514         TAILQ_INSERT_HEAD(&ctx->event, evt, entries);
00515         mutex_unlock(ctx);
00516 
00517         /* Increase the event counter, waking the thread */
00518         if (sem_post(&ctx->event_count) != 0)
00519                 err(1, "sem_post(3)");
00520 }

Generated on Wed Aug 22 23:15:42 2007 for pnotify by  doxygen 1.5.1