summaryrefslogtreecommitdiffstats
path: root/maildrop/alarm.C
blob: 3711888683ae3dc678eb2aa63d2e53c25cd69067 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include	"config.h"
#include	<iostream>


#if HAVE_UNISTD_H
#include	<unistd.h>
#else
extern "C" long alarm(long);
#endif

#include	<signal.h>
#include	"alarm.h"

Alarm *Alarm::first=0;
Alarm *Alarm::last=0;

Alarm::~Alarm()
{
	Cancel();
}

void Alarm::Unlink()
{
	set_interval=0;
	if (prev)	prev->next=next;
	else		first=next;
	if (next)	next->prev=prev;
	else		last=prev;
}

void Alarm::cancel_sig(unsigned seconds_left)
{
Alarm	*p;
Alarm	*alarm_chain=0;

	while ((p=first) != 0 && p->set_interval <= seconds_left)
			// Marginal case
	{
		p->Unlink();
		p->next=alarm_chain;
		alarm_chain=p;
	}

	for (p=first; p; p=p->next)
		p->set_interval -= seconds_left;

	while ((p=alarm_chain) != 0)
	{
		alarm_chain=p->next;
		p->handler();
	}
}

void Alarm::set_sig()
{
	if (!first)	return;
	signal(SIGALRM, &Alarm::alarm_func);
	alarm(first->set_interval);
}

void Alarm::alarm_func(int)
{
	if (first)	cancel_sig(first->set_interval);
	set_sig();
}

unsigned Alarm::sig_left()
{
	if (!first)	return (0);

unsigned n=alarm(0);

	return (n ? n <= first->set_interval ? first->set_interval - n:0:0);
}

void Alarm::Set(unsigned nseconds)
{
	Cancel();		// Just in case
	if (nseconds == 0)
	{
		handler();	// Fooey.
		return;
	}

	cancel_sig(sig_left());

Alarm	*p;

	for (p=first; p; p=p->next)
		if (p->set_interval > nseconds)
			break;

	if (!p)
	{
		next=0;
		if ((prev=last) != 0)
			prev->next=this;
		else
			first=this;
		last=this;
	}
	else
	{
		if ((prev=p->prev) != 0)
			prev->next=this;
		else
			first=this;
		next=p;
		p->prev=this;
	}
	set_interval=nseconds;
	set_sig();
}

void Alarm::Cancel()
{
	cancel_sig(sig_left());
	if (set_interval) Unlink();
	set_sig();
}