Blame lib/alarm.c

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