/* ----------------------------------------------------------------------- * * * alarm.c - alarm queue handling module. * * Copyright 2006 Ian Kent - All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, * USA; either version 2 of the License, or (at your option) any later * version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ #include #include "automount.h" struct alarm { time_t time; unsigned int cancel; struct autofs_point *ap; struct list_head list; }; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond; static LIST_HEAD(alarms); #define alarm_lock() \ do { \ int _alm_lock = pthread_mutex_lock(&mutex); \ if (_alm_lock) \ fatal(_alm_lock); \ } while (0) #define alarm_unlock() \ do { \ int _alm_unlock = pthread_mutex_unlock(&mutex); \ if (_alm_unlock) \ fatal(_alm_unlock); \ } while (0) /* Insert alarm entry on ordered list. */ int alarm_add(struct autofs_point *ap, time_t seconds) { struct list_head *head; struct list_head *p; struct alarm *new; time_t now = monotonic_time(NULL); time_t next_alarm = 0; unsigned int empty = 1; int status; if (!seconds) return 1; new = malloc(sizeof(struct alarm)); if (!new) return 0; new->ap = ap; new->cancel = 0; new->time = now + seconds; alarm_lock(); head = &alarms; /* Check if we have a pending alarm */ if (!list_empty(head)) { struct alarm *current; current = list_entry(head->next, struct alarm, list); next_alarm = current->time; empty = 0; } list_for_each(p, head) { struct alarm *this; this = list_entry(p, struct alarm, list); if (this->time >= new->time) { list_add_tail(&new->list, p); break; } } if (p == head) list_add_tail(&new->list, p); /* * Wake the alarm thread if it is not busy (ie. if the * alarms list was empty) or if the new alarm comes before * the alarm we are currently waiting on. */ if (empty || new->time < next_alarm) { status = pthread_cond_signal(&cond); if (status) fatal(status); } alarm_unlock(); return 1; } void alarm_delete(struct autofs_point *ap) { struct list_head *head; struct list_head *p; struct alarm *current; unsigned int signal_cancel = 0; int status; alarm_lock(); head = &alarms; if (list_empty(head)) { alarm_unlock(); return; } current = list_entry(head->next, struct alarm, list); p = head->next; while (p != head) { struct alarm *this; this = list_entry(p, struct alarm, list); p = p->next; if (ap == this->ap) { if (current != this) { list_del_init(&this->list); free(this); continue; } /* Mark as canceled */ this->cancel = 1; this->time = 0; signal_cancel = 1; } } if (signal_cancel) { status = pthread_cond_signal(&cond); if (status) fatal(status); } alarm_unlock(); return; } static void *alarm_handler(void *arg) { struct list_head *head; struct timespec expire; struct alarm *first; time_t now; int status; alarm_lock(); head = &alarms; while (1) { if (list_empty(head)) { /* No alarms, wait for one to be added */ status = pthread_cond_wait(&cond, &mutex); if (status) fatal(status); continue; } first = list_entry(head->next, struct alarm, list); now = monotonic_time(NULL); if (first->time > now) { struct timespec nsecs; /* * Wait for alarm to trigger or a new alarm * to be added. */ clock_gettime(CLOCK_MONOTONIC, &nsecs); expire.tv_sec = first->time; expire.tv_nsec = nsecs.tv_nsec; status = pthread_cond_timedwait(&cond, &mutex, &expire); if (status && status != ETIMEDOUT) fatal(status); } else { /* First alarm has triggered, run it */ list_del(&first->list); if (!first->cancel) { struct autofs_point *ap = first->ap; alarm_unlock(); st_add_task(ap, ST_EXPIRE); alarm_lock(); } free(first); } } /* Will never come here, so alarm_unlock is not necessary */ } int alarm_start_handler(void) { pthread_t thid; pthread_attr_t attrs; pthread_attr_t *pattrs = &attrs; pthread_condattr_t condattrs; int status; status = pthread_attr_init(pattrs); if (status) pattrs = NULL; else { pthread_attr_setdetachstate(pattrs, PTHREAD_CREATE_DETACHED); #ifdef _POSIX_THREAD_ATTR_STACKSIZE pthread_attr_setstacksize(pattrs, PTHREAD_STACK_MIN*4); #endif } status = pthread_condattr_init(&condattrs); if (status) fatal(status); status = pthread_condattr_setclock(&condattrs, CLOCK_MONOTONIC); if (status) fatal(status); status = pthread_cond_init(&cond, &condattrs); if (status) fatal(status); status = pthread_create(&thid, pattrs, alarm_handler, NULL); pthread_condattr_destroy(&condattrs); if (pattrs) pthread_attr_destroy(pattrs); return !status; }